[llvm] [LLVM] Successor count added to InstCount (PR #171670)

IƱaki V Arrechea via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 18 08:59:08 PST 2025


https://github.com/InakiVA updated https://github.com/llvm/llvm-project/pull/171670

>From d8cbcdc56a918d7d584b12a6c4fb36eb65bc1d0f Mon Sep 17 00:00:00 2001
From: Inaki Arrechea <inakiarrechea at google.com>
Date: Wed, 17 Dec 2025 19:16:24 +0000
Subject: [PATCH] Pass implementation of Function Properties Analysis - stats

---
 .../Analysis/FunctionPropertiesAnalysis.h     | 11 ++++
 .../Analysis/FunctionPropertiesAnalysis.cpp   | 51 ++++++++++++++++++-
 llvm/lib/Passes/PassBuilderPipelines.cpp      | 24 +++++----
 llvm/lib/Passes/PassRegistry.def              | 45 ++++++++++------
 llvm/test/Other/instcount.ll                  | 33 ++++++------
 5 files changed, 119 insertions(+), 45 deletions(-)

diff --git a/llvm/include/llvm/Analysis/FunctionPropertiesAnalysis.h b/llvm/include/llvm/Analysis/FunctionPropertiesAnalysis.h
index 3dc241c0124e4..a2923f74805da 100644
--- a/llvm/include/llvm/Analysis/FunctionPropertiesAnalysis.h
+++ b/llvm/include/llvm/Analysis/FunctionPropertiesAnalysis.h
@@ -127,6 +127,11 @@ class FunctionPropertiesInfo {
   int64_t CriticalEdgeCount = 0;
   int64_t ControlFlowEdgeCount = 0;
   int64_t UnconditionalBranchCount = 0;
+  int64_t ConditionalBranchCount = 0;
+  int64_t BranchInstructionCount = 0;
+  int64_t BranchSuccessorCount = 0;
+  int64_t SwitchInstructionCount = 0;
+  int64_t SwitchSuccessorCount = 0;
 
   // Call related instructions
   int64_t IntrinsicCount = 0;
@@ -179,6 +184,12 @@ class FunctionPropertiesPrinterPass
   static bool isRequired() { return true; }
 };
 
+/// Statistics pass for the FunctionPropertiesAnalysis results.
+struct FunctionPropertiesStatisticsPass
+    : PassInfoMixin<FunctionPropertiesStatisticsPass> {
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
 /// Correctly update FunctionPropertiesInfo post-inlining. A
 /// FunctionPropertiesUpdater keeps the state necessary for tracking the changes
 /// llvm::InlineFunction makes. The idea is that inlining will at most modify
diff --git a/llvm/lib/Analysis/FunctionPropertiesAnalysis.cpp b/llvm/lib/Analysis/FunctionPropertiesAnalysis.cpp
index c52a6de2bb71e..9f7bf6b6387d2 100644
--- a/llvm/lib/Analysis/FunctionPropertiesAnalysis.cpp
+++ b/llvm/lib/Analysis/FunctionPropertiesAnalysis.cpp
@@ -14,6 +14,7 @@
 #include "llvm/Analysis/FunctionPropertiesAnalysis.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/IR/CFG.h"
 #include "llvm/IR/Constants.h"
@@ -26,6 +27,19 @@
 
 using namespace llvm;
 
+#define DEBUG_TYPE "statscount"
+
+STATISTIC(TotalBlocks, "Number of basic blocks");
+STATISTIC(TotalInsts, "Number of instructions (of all types)");
+STATISTIC(TotalSuccs, "Number of basic block successors");
+STATISTIC(TotalUncondBranchInsts,
+          "Number of unconditional branch instructions");
+STATISTIC(TotalCondBranchInsts, "Number of conditional branch instructions");
+STATISTIC(TotalBranchInsts, "Number of branch instructions");
+STATISTIC(TotalBranchSuccs, "Number of branch successors");
+STATISTIC(TotalSwitchInsts, "Number of switch instructions");
+STATISTIC(TotalSwitchSuccs, "Number of switch successors");
+
 namespace llvm {
 LLVM_ABI cl::opt<bool> EnableDetailedFunctionProperties(
     "enable-detailed-function-properties", cl::Hidden, cl::init(false),
@@ -124,9 +138,19 @@ void FunctionPropertiesInfo::updateForBB(const BasicBlock &BB,
 
     ControlFlowEdgeCount += Direction * SuccessorCount;
 
-    if (const auto *BI = dyn_cast<BranchInst>(BB.getTerminator())) {
-      if (!BI->isConditional())
+    const Instruction *TI = BB.getTerminator();
+    const int64_t InstructionSuccessorCount = TI->getNumSuccessors();
+    if (isa<BranchInst>(TI)) {
+      BranchInstructionCount += Direction;
+      BranchSuccessorCount += Direction * InstructionSuccessorCount;
+      const auto *BI = dyn_cast<BranchInst>(TI);
+      if (BI->isConditional())
+        ConditionalBranchCount += Direction;
+      else
         UnconditionalBranchCount += Direction;
+    } else if (isa<SwitchInst>(TI)) {
+      SwitchInstructionCount += Direction;
+      SwitchSuccessorCount += Direction * InstructionSuccessorCount;
     }
 
     for (const Instruction &I : BB.instructionsWithoutDebug()) {
@@ -362,6 +386,11 @@ void FunctionPropertiesInfo::print(raw_ostream &OS) const {
     PRINT_PROPERTY(CriticalEdgeCount)
     PRINT_PROPERTY(ControlFlowEdgeCount)
     PRINT_PROPERTY(UnconditionalBranchCount)
+    PRINT_PROPERTY(ConditionalBranchCount)
+    PRINT_PROPERTY(BranchInstructionCount)
+    PRINT_PROPERTY(BranchSuccessorCount)
+    PRINT_PROPERTY(SwitchInstructionCount)
+    PRINT_PROPERTY(SwitchSuccessorCount)
     PRINT_PROPERTY(IntrinsicCount)
     PRINT_PROPERTY(DirectCallCount)
     PRINT_PROPERTY(IndirectCallCount)
@@ -396,6 +425,24 @@ FunctionPropertiesPrinterPass::run(Function &F, FunctionAnalysisManager &AM) {
   return PreservedAnalyses::all();
 }
 
+PreservedAnalyses
+FunctionPropertiesStatisticsPass::run(Function &F,
+                                      FunctionAnalysisManager &AM) {
+  LLVM_DEBUG(dbgs() << "STATSCOUNT: running on function " << F.getName()
+                    << "\n");
+  auto AnalysisResults = AM.getResult<FunctionPropertiesAnalysis>(F);
+  TotalBlocks += AnalysisResults.BasicBlockCount;
+  TotalInsts += AnalysisResults.TotalInstructionCount;
+  TotalSuccs += AnalysisResults.ControlFlowEdgeCount;
+  TotalUncondBranchInsts += AnalysisResults.UnconditionalBranchCount;
+  TotalCondBranchInsts += AnalysisResults.ConditionalBranchCount;
+  TotalBranchInsts += AnalysisResults.BranchInstructionCount;
+  TotalBranchSuccs += AnalysisResults.BranchSuccessorCount;
+  TotalSwitchInsts += AnalysisResults.SwitchInstructionCount;
+  TotalSwitchSuccs += AnalysisResults.SwitchSuccessorCount;
+  return PreservedAnalyses::all();
+}
+
 FunctionPropertiesUpdater::FunctionPropertiesUpdater(
     FunctionPropertiesInfo &FPI, CallBase &CB)
     : FPI(FPI), CallSiteBB(*CB.getParent()), Caller(*CallSiteBB.getParent()) {
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 67b9a61cc576f..16fa233c6a727 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -19,6 +19,7 @@
 #include "llvm/Analysis/BasicAliasAnalysis.h"
 #include "llvm/Analysis/CGSCCPassManager.h"
 #include "llvm/Analysis/CtxProfAnalysis.h"
+#include "llvm/Analysis/FunctionPropertiesAnalysis.h"
 #include "llvm/Analysis/GlobalsModRef.h"
 #include "llvm/Analysis/InlineAdvisor.h"
 #include "llvm/Analysis/InstCount.h"
@@ -30,6 +31,7 @@
 #include "llvm/Passes/OptimizationLevel.h"
 #include "llvm/Passes/PassBuilder.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/PGOOptions.h"
 #include "llvm/Support/VirtualFileSystem.h"
@@ -191,9 +193,9 @@ static cl::opt<bool> EnablePostPGOLoopRotation(
     "enable-post-pgo-loop-rotation", cl::init(true), cl::Hidden,
     cl::desc("Run the loop rotation transformation after PGO instrumentation"));
 
-static cl::opt<bool> EnableGlobalAnalyses(
-    "enable-global-analyses", cl::init(true), cl::Hidden,
-    cl::desc("Enable inter-procedural analyses"));
+static cl::opt<bool>
+    EnableGlobalAnalyses("enable-global-analyses", cl::init(true), cl::Hidden,
+                         cl::desc("Enable inter-procedural analyses"));
 
 static cl::opt<bool> RunPartialInlining("enable-partial-inlining",
                                         cl::init(false), cl::Hidden,
@@ -413,8 +415,11 @@ void PassBuilder::invokePipelineEarlySimplificationEPCallbacks(
 static void addAnnotationRemarksPass(ModulePassManager &MPM) {
   MPM.addPass(createModuleToFunctionPassAdaptor(AnnotationRemarksPass()));
   // Count the types of instructions used
-  if (AreStatisticsEnabled())
-    MPM.addPass(createModuleToFunctionPassAdaptor(InstCountPass()));
+  if (AreStatisticsEnabled()) {
+    // MPM.addPass(createModuleToFunctionPassAdaptor(InstCountPass()));
+    MPM.addPass(
+        createModuleToFunctionPassAdaptor(FunctionPropertiesStatisticsPass()));
+  }
 }
 
 // Helper to check if the current compilation phase is preparing for LTO
@@ -1181,11 +1186,10 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
   // and prior to optimizing globals.
   // FIXME: This position in the pipeline hasn't been carefully considered in
   // years, it should be re-analyzed.
-  MPM.addPass(IPSCCPPass(
-              IPSCCPOptions(/*AllowFuncSpec=*/
-                            Level != OptimizationLevel::Os &&
-                            Level != OptimizationLevel::Oz &&
-                            !isLTOPreLink(Phase))));
+  MPM.addPass(IPSCCPPass(IPSCCPOptions(/*AllowFuncSpec=*/
+                                       Level != OptimizationLevel::Os &&
+                                       Level != OptimizationLevel::Oz &&
+                                       !isLTOPreLink(Phase))));
 
   // Attach metadata to indirect call sites indicating the set of functions
   // they may target at run-time. This should follow IPSCCP.
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index cf998f29ef38c..ce5436a5417fd 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -257,25 +257,29 @@ MODULE_PASS_WITH_PARAMS(
     parseStructuralHashPrinterPassOptions, "detailed;call-target-ignored")
 
 MODULE_PASS_WITH_PARAMS(
-    "default", "", [&](OptimizationLevel L) {
+    "default", "",
+    [&](OptimizationLevel L) {
       setupOptionsForPipelineAlias(PTO, L);
       return buildPerModuleDefaultPipeline(L);
     },
     parseOptLevelParam, "O0;O1;O2;O3;Os;Oz")
 MODULE_PASS_WITH_PARAMS(
-    "thinlto-pre-link", "", [&](OptimizationLevel L) {
+    "thinlto-pre-link", "",
+    [&](OptimizationLevel L) {
       setupOptionsForPipelineAlias(PTO, L);
       return buildThinLTOPreLinkDefaultPipeline(L);
     },
     parseOptLevelParam, "O0;O1;O2;O3;Os;Oz")
 MODULE_PASS_WITH_PARAMS(
-    "thinlto", "", [&](OptimizationLevel L) {
+    "thinlto", "",
+    [&](OptimizationLevel L) {
       setupOptionsForPipelineAlias(PTO, L);
       return buildThinLTODefaultPipeline(L, nullptr);
     },
     parseOptLevelParam, "O0;O1;O2;O3;Os;Oz")
 MODULE_PASS_WITH_PARAMS(
-    "lto-pre-link", "", [&](OptimizationLevel L) {
+    "lto-pre-link", "",
+    [&](OptimizationLevel L) {
       setupOptionsForPipelineAlias(PTO, L);
       if (PTO.UnifiedLTO) {
         // When UnifiedLTO is enabled, use the ThinLTO pre-link pipeline. This
@@ -287,13 +291,15 @@ MODULE_PASS_WITH_PARAMS(
     },
     parseOptLevelParam, "O0;O1;O2;O3;Os;Oz")
 MODULE_PASS_WITH_PARAMS(
-    "lto", "", [&](OptimizationLevel L) {
+    "lto", "",
+    [&](OptimizationLevel L) {
       setupOptionsForPipelineAlias(PTO, L);
       return buildLTODefaultPipeline(L, nullptr);
     },
     parseOptLevelParam, "O0;O1;O2;O3;Os;Oz")
 MODULE_PASS_WITH_PARAMS(
-    "fatlto-pre-link", "", [&](FatLTOOptions Opts) {
+    "fatlto-pre-link", "",
+    [&](FatLTOOptions Opts) {
       setupOptionsForPipelineAlias(PTO, Opts.OptLevel);
       return buildFatLTODefaultPipeline(Opts.OptLevel, Opts.ThinLTO,
                                         Opts.EmitSummary);
@@ -543,6 +549,7 @@ FUNCTION_PASS("sjlj-eh-prepare", SjLjEHPreparePass(TM))
 FUNCTION_PASS("slp-vectorizer", SLPVectorizerPass())
 FUNCTION_PASS("slsr", StraightLineStrengthReducePass())
 FUNCTION_PASS("stack-protector", StackProtectorPass(*TM))
+FUNCTION_PASS("statscount", FunctionPropertiesStatisticsPass())
 FUNCTION_PASS("strip-gc-relocates", StripGCRelocates())
 FUNCTION_PASS("tailcallelim", TailCallElimPass())
 FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass())
@@ -583,7 +590,9 @@ FUNCTION_PASS_WITH_PARAMS(
     parseEarlyCSEPassOptions, "memssa")
 FUNCTION_PASS_WITH_PARAMS(
     "drop-unnecessary-assumes", "DropUnnecessaryAssumesPass",
-    [](bool DropDereferenceable) { return DropUnnecessaryAssumesPass(DropDereferenceable); },
+    [](bool DropDereferenceable) {
+      return DropUnnecessaryAssumesPass(DropDereferenceable);
+    },
     parseDropUnnecessaryAssumesPassOptions, "drop-deref")
 FUNCTION_PASS_WITH_PARAMS(
     "ee-instrument", "EntryExitInstrumenterPass",
@@ -731,9 +740,7 @@ FUNCTION_PASS_WITH_PARAMS(
     "trap;rt;rt-abort;min-rt;min-rt-abort;merge;guard=N")
 FUNCTION_PASS_WITH_PARAMS(
     "expand-fp", "ExpandFpPass",
-    [TM = TM](CodeGenOptLevel OL) {
-      return ExpandFpPass(*TM, OL);
-    },
+    [TM = TM](CodeGenOptLevel OL) { return ExpandFpPass(*TM, OL); },
     parseExpandFpOptions, "O0;O1;O2;O3")
 
 #undef FUNCTION_PASS_WITH_PARAMS
@@ -821,27 +828,33 @@ MODULE_CALLBACK("pipeline-start-callbacks", invokePipelineStartEPCallbacks)
 
 // There are some full lto specific ones that are ignored here for now
 #ifdef MODULE_LTO_CALLBACK
-MODULE_LTO_CALLBACK("pipeline-early-simplification-callbacks", invokePipelineEarlySimplificationEPCallbacks)
-MODULE_LTO_CALLBACK("optimizer-early-callbacks", invokeOptimizerEarlyEPCallbacks)
+MODULE_LTO_CALLBACK("pipeline-early-simplification-callbacks",
+                    invokePipelineEarlySimplificationEPCallbacks)
+MODULE_LTO_CALLBACK("optimizer-early-callbacks",
+                    invokeOptimizerEarlyEPCallbacks)
 MODULE_LTO_CALLBACK("optimizer-last-callbacks", invokeOptimizerLastEPCallbacks)
 #endif
 #undef MODULE_LTO_CALLBACK
 
 #ifdef FUNCTION_CALLBACK
 FUNCTION_CALLBACK("peephole-callbacks", invokePeepholeEPCallbacks)
-FUNCTION_CALLBACK("scalar-optimizer-late-callbacks", invokeScalarOptimizerLateEPCallbacks)
-FUNCTION_CALLBACK("vectorizer-start-callbacks", invokeVectorizerStartEPCallbacks)
+FUNCTION_CALLBACK("scalar-optimizer-late-callbacks",
+                  invokeScalarOptimizerLateEPCallbacks)
+FUNCTION_CALLBACK("vectorizer-start-callbacks",
+                  invokeVectorizerStartEPCallbacks)
 FUNCTION_CALLBACK("vectorizer-end-callbacks", invokeVectorizerEndEPCallbacks)
 #endif
 #undef FUNCTION_CALLBACK
 
 #ifdef CGSCC_CALLBACK
-CGSCC_CALLBACK("cgscc-optimizer-late-callbacks", invokeCGSCCOptimizerLateEPCallbacks)
+CGSCC_CALLBACK("cgscc-optimizer-late-callbacks",
+               invokeCGSCCOptimizerLateEPCallbacks)
 #endif
 #undef CGSCC_CALLBACK
 
 #ifdef LOOP_CALLBACK
-LOOP_CALLBACK("late-loop-optimizations-callbacks", invokeLateLoopOptimizationsEPCallbacks)
+LOOP_CALLBACK("late-loop-optimizations-callbacks",
+              invokeLateLoopOptimizationsEPCallbacks)
 LOOP_CALLBACK("loop-optimizer-end-callbacks", invokeLoopOptimizerEndEPCallbacks)
 #endif
 #undef LOOP_CALLBACK
diff --git a/llvm/test/Other/instcount.ll b/llvm/test/Other/instcount.ll
index 931d547371958..2a8c196445e34 100644
--- a/llvm/test/Other/instcount.ll
+++ b/llvm/test/Other/instcount.ll
@@ -1,21 +1,20 @@
-; REQUIRES: asserts
-; RUN: opt -stats -passes=instcount -disable-output < %s 2>&1 | FileCheck %s
-; RUN: opt -stats -passes='thinlto<O3>' -disable-output < %s 2>&1 | FileCheck %s
-; RUN: opt -stats -passes='thinlto-pre-link<O2>' -disable-output < %s 2>&1 | FileCheck %s
-; RUN: opt -stats -passes='lto<O1>' -disable-output < %s 2>&1 | FileCheck %s
-; RUN: opt -stats -passes='lto-pre-link<Os>' -disable-output < %s 2>&1 | FileCheck %s
-; RUN: opt -stats -O3 -disable-output < %s 2>&1 | FileCheck %s
-; RUN: opt -stats -O0 -disable-output < %s 2>&1 | FileCheck %s
-
-; CHECK-DAG: 8 instcount - Number of Br insts
-; CHECK-DAG: 6 instcount - Number of Call insts
-; CHECK-DAG: 2 instcount - Number of ICmp insts
-; CHECK-DAG: 1 instcount - Number of Ret insts
-; CHECK-DAG: 1 instcount - Number of Switch insts
-; CHECK-DAG: 10 instcount - Number of basic blocks
-; CHECK-DAG: 1 instcount - Number of non-external functions
-; CHECK-DAG: 18 instcount - Number of instructions (of all types)
+; REQUIRES asserts
+; RUN: opt -stats -enable-detailed-function-properties -disable-output -passes=statscount < %s 2>&1 | FileCheck %s
+; RUN: opt -stats -enable-detailed-function-properties -disable-output -passes='thinlto<O3>'< %s 2>&1 | FileCheck %s
+; RUN: opt -stats -enable-detailed-function-properties -disable-output -passes='thinlto-pre-link<O2>' < %s 2>&1 | FileCheck %s
+; RUN: opt -stats -enable-detailed-function-properties -disable-output -passes='lto<O1>' < %s 2>&1 | FileCheck %s
+; RUN: opt -stats -enable-detailed-function-properties -disable-output -passes='default<O3>' < %s 2>&1 | FileCheck %s
+; RUN: opt -stats -enable-detailed-function-properties -disable-output -passes='default<O0>' < %s 2>&1 | FileCheck %s
 
+; CHECK-DAG: 10 statscount - Number of basic blocks
+; CHECK-DAG: 8 statscount - Number of branch instructions
+; CHECK-DAG: 10 statscount - Number of branch successors
+; CHECK-DAG: 2 statscount - Number of conditional branch instructions
+; CHECK-DAG: 18 statscount - Number of instructions (of all types)
+; CHECK-DAG: 14 statscount - Number of basic block successors
+; CHECK-DAG: 1 statscount - Number of switch instructions
+; CHECK-DAG: 4 statscount - Number of switch successors
+; CHECK-DAG: 6 statscount - Number of unconditional branch instructions
 
 define void @foo(i32 %i, i32 %j, i32 %n) {
 entry:



More information about the llvm-commits mailing list