Skip to content

[LLVM] Successor count added to InstCount#171670

Merged
alinas merged 10 commits intollvm:mainfrom
InakiVA:4metrics
Jan 6, 2026
Merged

[LLVM] Successor count added to InstCount#171670
alinas merged 10 commits intollvm:mainfrom
InakiVA:4metrics

Conversation

@InakiVA
Copy link
Contributor

@InakiVA InakiVA commented Dec 10, 2025

Counts the number of Basic Block successors when stats are enabled
We want to track this metric for a project in which we analyze the effects of having LLVM Intermediate Representation (IR) code with a high number of branches. We plan to use these capabilities to learn how enabling memory safety features increases the branchiness in LLVM IR and further how it affects binary compilation time and the resulting binary performance.

@llvmbot llvmbot added the llvm:analysis Includes value tracking, cost tables and constant folding label Dec 10, 2025
@github-actions
Copy link

github-actions bot commented Dec 10, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@llvmbot
Copy link
Member

llvmbot commented Dec 10, 2025

@llvm/pr-subscribers-llvm-ir
@llvm/pr-subscribers-mlgo

@llvm/pr-subscribers-llvm-analysis

Author: Iñaki V Arrechea (InakiVA)

Changes

Counts the number of Basic Block successors when stats are enabled


Full diff: https://github.com/llvm/llvm-project/pull/171670.diff

3 Files Affected:

  • (modified) llvm/lib/Analysis/InstCount.cpp (+6-1)
  • (modified) llvm/lib/Passes/PassBuilderPipelines.cpp (+6)
  • (added) llvm/test/Other/instcount.ll (+76)
diff --git a/llvm/lib/Analysis/InstCount.cpp b/llvm/lib/Analysis/InstCount.cpp
index b43c9cd074b9d..f7831c49c34f2 100644
--- a/llvm/lib/Analysis/InstCount.cpp
+++ b/llvm/lib/Analysis/InstCount.cpp
@@ -24,6 +24,7 @@ using namespace llvm;
 STATISTIC(TotalInsts, "Number of instructions (of all types)");
 STATISTIC(TotalBlocks, "Number of basic blocks");
 STATISTIC(TotalFuncs, "Number of non-external functions");
+STATISTIC(TotalBlockSucs, "Number of basic block successors");
 
 #define HANDLE_INST(N, OPCODE, CLASS)                                          \
   STATISTIC(Num##OPCODE##Inst, "Number of " #OPCODE " insts");
@@ -35,7 +36,11 @@ class InstCount : public InstVisitor<InstCount> {
   friend class InstVisitor<InstCount>;
 
   void visitFunction(Function &F) { ++TotalFuncs; }
-  void visitBasicBlock(BasicBlock &BB) { ++TotalBlocks; }
+  void visitBasicBlock(BasicBlock &BB) {
+    Instruction *I = BB.getTerminator();
+    TotalBlockSucs += I->getNumSuccessors();
+    ++TotalBlocks;
+  }
 
 #define HANDLE_INST(N, OPCODE, CLASS)                                          \
   void visit##OPCODE(CLASS &) {                                                \
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 4de527d9ef85e..edafde9f68279 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -21,6 +21,7 @@
 #include "llvm/Analysis/CtxProfAnalysis.h"
 #include "llvm/Analysis/GlobalsModRef.h"
 #include "llvm/Analysis/InlineAdvisor.h"
+#include "llvm/Analysis/InstCount.h"
 #include "llvm/Analysis/ProfileSummaryInfo.h"
 #include "llvm/Analysis/ScopedNoAliasAA.h"
 #include "llvm/Analysis/TypeBasedAliasAnalysis.h"
@@ -1737,8 +1738,13 @@ PassBuilder::buildPerModuleDefaultPipeline(OptimizationLevel Level,
   // Emit annotation remarks.
   addAnnotationRemarksPass(MPM);
 
+  // Count the types of instructions used
+  if (AreStatisticsEnabled())
+    MPM.addPass(createModuleToFunctionPassAdaptor(InstCountPass()));
+
   if (isLTOPreLink(Phase))
     addRequiredLTOPreLinkPasses(MPM);
+
   return MPM;
 }
 
diff --git a/llvm/test/Other/instcount.ll b/llvm/test/Other/instcount.ll
new file mode 100644
index 0000000000000..7c19668857538
--- /dev/null
+++ b/llvm/test/Other/instcount.ll
@@ -0,0 +1,76 @@
+; RUN: opt -stats -passes=instcount < %s 2>&1 | FileCheck %s --check-prefix=UNOPT
+; RUN: opt -stats -O3 < %s 2>&1 | FileCheck %s --check-prefix=OPT
+
+; --- FIRST RUN (UNOPTIMIZED) ---
+; UNOPT-DAG: 8 instcount - Number of Br insts
+; UNOPT-DAG: 6 instcount - Number of Call insts
+; UNOPT-DAG: 2 instcount - Number of ICmp insts
+; UNOPT-DAG: 1 instcount - Number of Ret insts
+; UNOPT-DAG: 1 instcount - Number of Switch insts
+; UNOPT-DAG: 10 instcount - Number of basic blocks
+; UNOPT-DAG: 1 instcount - Number of non-external functions
+; UNOPT-DAG: 18 instcount - Number of instructions (of all types)
+; UNOPT-DAG: 14 instcount - Number of basic block successors
+
+; --- SECOND RUN (OPTIMIZED) ---
+; OPT-DAG: 8 instcount - Number of Br insts
+; OPT-DAG: 6 instcount - Number of Call insts
+; OPT-DAG: 2 instcount - Number of ICmp insts
+; OPT-DAG: 1 instcount - Number of Ret insts
+; OPT-DAG: 1 instcount - Number of Switch insts
+; OPT-DAG: 10 instcount - Number of basic blocks
+; OPT-DAG: 1 instcount - Number of non-external functions
+; OPT-DAG: 18 instcount - Number of instructions (of all types)
+; OPT-DAG: 14 instcount - Number of basic block successors
+
+
+define dso_local void @foo(i32 noundef %i, i32 noundef %j, i32 noundef %n) {
+entry:
+  %cmp = icmp slt i32 %i, %j
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:
+  call void @f()
+  br label %if.end
+
+if.end:
+  switch i32 %i, label %sw.default [
+    i32 1, label %sw.bb
+    i32 2, label %sw.bb1
+    i32 3, label %sw.bb1
+  ]
+
+sw.bb:
+  call void @g()
+  br label %sw.epilog
+
+sw.bb1:
+  call void @h()
+  br label %sw.epilog
+
+sw.default:
+  call void @k()
+  br label %sw.epilog
+
+sw.epilog:
+  %cmp2 = icmp sgt i32 %i, %n
+  br i1 %cmp2, label %if.then3, label %if.else
+
+if.then3:
+  call void @l()
+  br label %if.end4
+
+if.else:
+  call void @m()
+  br label %if.end4
+
+if.end4:
+  ret void
+}
+
+declare void @f()
+declare void @g()
+declare void @h()
+declare void @k()
+declare void @l()
+declare void @m()

Copy link
Contributor

@boomanaiden154 boomanaiden154 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes more sense to do this within FunctionPropertiesAnalysis.

int64_t BasicBlocksWithSingleSuccessor = 0;

We already collect some data around successors, so that might cover what you need.

@alinas
Copy link
Contributor

alinas commented Dec 12, 2025

I think it makes more sense to do this within FunctionPropertiesAnalysis.

int64_t BasicBlocksWithSingleSuccessor = 0;

We already collect some data around successors, so that might cover what you need.

Based on #171658, this change is looking for collecting an additional statistic for LLVM IR structure, beyond instruction count. One the one hand, counting successors doesn't fit in with counting instructions, on the other, adding another analysis pass to the default pipelines (either a new one, or extending FunctionPropertiesAnalysis to expose those counts as statistics) seems like unnecessary overhead.
I can see arguments both ways, but considering this pass is already counting blocks and functions, I'd opt for having this added in as is.

@InakiVA InakiVA force-pushed the 4metrics branch 2 times, most recently from b03e923 to 897837e Compare December 12, 2025 19:11
@boomanaiden154
Copy link
Contributor

Based on #171658, this change is looking for collecting an additional statistic for LLVM IR structure, beyond instruction count. One the one hand, counting successors doesn't fit in with counting instructions, on the other, adding another analysis pass to the default pipelines (either a new one, or extending FunctionPropertiesAnalysis to expose those counts as statistics) seems like unnecessary overhead.

FunctionPropertiesAnalysis is already in the default pipeline (it's used by the ML inliner). It also has supported for what we call "detailed" statistics that are gated (under a CLI flag in the printer pass, under a pass option in the analysis if I recall correctly). Requiring the analysis here and exposing them as statistics could make sense. I think we have macros already that would make that easy.

If it's just this feature that people want, I'm fine landing this as-is. But if the plan is to add a bunch of similar features, I would probably want something more principled to avoid putting this sort of infrastructure in a bunch of places.

@nikic
Copy link
Contributor

nikic commented Dec 13, 2025

Please make sure that your PR descriptions include not just what you are doing, but why you are doing it.

@InakiVA InakiVA force-pushed the 4metrics branch 3 times, most recently from f7615f0 to 132e440 Compare December 18, 2025 18:56
@InakiVA InakiVA requested a review from alinas December 19, 2025 17:01
Copy link
Contributor

@alinas alinas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lg otherwise.

@InakiVA InakiVA force-pushed the 4metrics branch 2 times, most recently from f9cf953 to c14da93 Compare January 2, 2026 19:49
@alinas alinas merged commit 701040d into llvm:main Jan 6, 2026
10 checks passed
int64_t CriticalEdgeCount = 0;
int64_t ControlFlowEdgeCount = 0;
int64_t UnconditionalBranchCount = 0;
int64_t ConditionalBranchCount = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use FunctionProperties.def here as well?

@InakiVA InakiVA deleted the 4metrics branch February 24, 2026 00:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:analysis Includes value tracking, cost tables and constant folding llvm:ir mlgo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants