Skip to content

Conversation

@InakiVA
Copy link
Contributor

@InakiVA InakiVA commented Dec 8, 2025

No description provided.

@llvmbot
Copy link
Member

llvmbot commented Dec 8, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Iñaki V Arrechea (InakiVA)

Changes

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

7 Files Affected:

  • (added) llvm/include/llvm/Transforms/Utils/CountInstructions.h (+29)
  • (modified) llvm/lib/Passes/PassBuilder.cpp (+1)
  • (modified) llvm/lib/Passes/PassBuilderPipelines.cpp (+2)
  • (modified) llvm/lib/Passes/PassRegistry.def (+1)
  • (modified) llvm/lib/Transforms/Utils/CMakeLists.txt (+1)
  • (added) llvm/lib/Transforms/Utils/CountInstructions.cpp (+63)
  • (added) llvm/test/Other/count-instructions.ll (+104)
diff --git a/llvm/include/llvm/Transforms/Utils/CountInstructions.h b/llvm/include/llvm/Transforms/Utils/CountInstructions.h
new file mode 100644
index 0000000000000..d22c050e9fd81
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/CountInstructions.h
@@ -0,0 +1,29 @@
+//===- CountInstructions.h --------------------------------------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_COUNTINSTRUCTIONS_H
+#define LLVM_TRANSFORMS_UTILS_COUNTINSTRUCTIONS_H
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class Function;
+
+struct CountInstructionsPass : PassInfoMixin<CountInstructionsPass> {
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &);
+
+private:
+  StringMap<uint32_t> Counts;
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_UTILS_COUNTINSTRUCTIONS_H
\ No newline at end of file
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index f5281ea69b512..243d495089065 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -347,6 +347,7 @@
 #include "llvm/Transforms/Utils/BreakCriticalEdges.h"
 #include "llvm/Transforms/Utils/CanonicalizeAliases.h"
 #include "llvm/Transforms/Utils/CanonicalizeFreezeInLoops.h"
+#include "llvm/Transforms/Utils/CountInstructions.h"
 #include "llvm/Transforms/Utils/CountVisits.h"
 #include "llvm/Transforms/Utils/DXILUpgrade.h"
 #include "llvm/Transforms/Utils/Debugify.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 4de527d9ef85e..6e65e347a8af7 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -135,6 +135,7 @@
 #include "llvm/Transforms/Utils/AddDiscriminators.h"
 #include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
 #include "llvm/Transforms/Utils/CanonicalizeAliases.h"
+#include "llvm/Transforms/Utils/CountInstructions.h"
 #include "llvm/Transforms/Utils/CountVisits.h"
 #include "llvm/Transforms/Utils/EntryExitInstrumenter.h"
 #include "llvm/Transforms/Utils/ExtraPassManager.h"
@@ -1737,6 +1738,7 @@ PassBuilder::buildPerModuleDefaultPipeline(OptimizationLevel Level,
   // Emit annotation remarks.
   addAnnotationRemarksPass(MPM);
 
+  MPM.addPass(createModuleToFunctionPassAdaptor(CountInstructionsPass()));
   if (isLTOPreLink(Phase))
     addRequiredLTOPreLinkPasses(MPM);
   return MPM;
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index cf998f29ef38c..3d7c33915b47d 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -420,6 +420,7 @@ FUNCTION_PASS("consthoist", ConstantHoistingPass())
 FUNCTION_PASS("constraint-elimination", ConstraintEliminationPass())
 FUNCTION_PASS("coro-elide", CoroElidePass())
 FUNCTION_PASS("correlated-propagation", CorrelatedValuePropagationPass())
+FUNCTION_PASS("count-instructions", CountInstructionsPass())
 FUNCTION_PASS("count-visits", CountVisitsPass())
 FUNCTION_PASS("dce", DCEPass())
 FUNCTION_PASS("declare-to-assign", llvm::AssignmentTrackingPass())
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index f367ca2fdf56b..198f84d89279a 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -18,6 +18,7 @@ add_llvm_component_library(LLVMTransformUtils
   CodeMoverUtils.cpp
   ControlFlowUtils.cpp
   CtorUtils.cpp
+  CountInstructions.cpp
   CountVisits.cpp
   Debugify.cpp
   DebugSSAUpdater.cpp
diff --git a/llvm/lib/Transforms/Utils/CountInstructions.cpp b/llvm/lib/Transforms/Utils/CountInstructions.cpp
new file mode 100644
index 0000000000000..ad7290d5d1094
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/CountInstructions.cpp
@@ -0,0 +1,63 @@
+//===- CountInstructions.cpp
+//----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/CountInstructions.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Support/Casting.h"
+#include <cstdint>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "count-instructions"
+
+STATISTIC(TotalBasicBlocks, "Number of basic blocks");
+STATISTIC(TotalInstructions, "Number of total instructions");
+STATISTIC(TotalBranchInstructions, "Number of branch instructions");
+STATISTIC(TotalSwitchInstructions, "Number of switch instructions");
+STATISTIC(TotalSuccessors, "Number of basic block successors");
+STATISTIC(TotalBranchSuccessors, "Number of branch successors");
+STATISTIC(TotalSwitchSuccessors, "Number of switch successors");
+PreservedAnalyses CountInstructionsPass::run(Function &F,
+                                             FunctionAnalysisManager &) {
+  uint32_t CountBasicBlocks = 0;
+  uint32_t CountInstructions = 0;
+  uint32_t CountBranchInstructions = 0;
+  uint32_t CountSwitchInstructions = 0;
+  uint32_t CountSuccessors = 0;
+  uint32_t CountBranchSuccessors = 0;
+  uint32_t CountSwitchSuccessors = 0;
+
+  for (BasicBlock &BB : F) {
+    CountBasicBlocks++;
+    Instruction *I = BB.getTerminator();
+    CountSuccessors += I->getNumSuccessors();
+    if (isa<BranchInst>(I)) {
+      CountBranchInstructions++;
+      CountBranchSuccessors += I->getNumSuccessors();
+    } else if (isa<SwitchInst>(I)) {
+      CountSwitchInstructions++;
+      CountSwitchSuccessors += I->getNumSuccessors();
+    }
+    CountInstructions += BB.size();
+  }
+  TotalInstructions += CountInstructions;
+  TotalBasicBlocks += CountBasicBlocks;
+  TotalBranchInstructions += CountBranchInstructions;
+  TotalSwitchInstructions += CountSwitchInstructions;
+  TotalSuccessors += CountSuccessors;
+  TotalBranchSuccessors += CountBranchSuccessors;
+  TotalSwitchSuccessors += CountSwitchSuccessors;
+
+  return PreservedAnalyses::all();
+}
\ No newline at end of file
diff --git a/llvm/test/Other/count-instructions.ll b/llvm/test/Other/count-instructions.ll
new file mode 100644
index 0000000000000..db5072e54a0a1
--- /dev/null
+++ b/llvm/test/Other/count-instructions.ll
@@ -0,0 +1,104 @@
+; REQUIRES: asserts, stats
+; RUN: opt -stats -passes=count-instructions < %s
+
+define dso_local noundef i32 @add(i32 noundef %n) {
+entry:
+  %n.addr = alloca i32, align 4
+  store i32 %n, ptr %n.addr, align 4
+  %0 = load i32, ptr %n.addr, align 4
+  %add = add nsw i32 %0, 1
+  ret i32 %add
+}
+
+define dso_local void @f(i32 noundef %i) {
+entry:
+  %i.addr = alloca i32, align 4
+  %x = alloca i32, align 4
+  store i32 %i, ptr %i.addr, align 4
+  store i32 0, ptr %x, align 4
+  %0 = load i32, ptr %i.addr, align 4
+  switch i32 %0, label %sw.epilog [
+    i32 0, label %sw.bb
+    i32 10, label %sw.bb9
+    i32 1, label %sw.bb9
+    i32 2, label %sw.bb10
+    i32 3, label %sw.bb11
+    i32 4, label %sw.bb12
+  ]
+
+sw.bb:
+  %call = call noundef i32 @add(i32 noundef 9)
+  store i32 %call, ptr %x, align 4
+  %1 = load i32, ptr %x, align 4
+  %cmp = icmp eq i32 %1, 0
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:
+  store i32 1, ptr %x, align 4
+  br label %if.end
+
+if.end:
+  %2 = load i32, ptr %x, align 4
+  %call1 = call noundef i32 @add(i32 noundef %2)
+  store i32 %call1, ptr %x, align 4
+  %3 = load i32, ptr %x, align 4
+  %cmp2 = icmp eq i32 %3, 0
+  br i1 %cmp2, label %if.then3, label %if.else
+
+if.then3:
+  store i32 1, ptr %x, align 4
+  br label %if.end8
+
+if.else:
+  %4 = load i32, ptr %x, align 4
+  %cmp4 = icmp eq i32 %4, 1
+  br i1 %cmp4, label %if.then5, label %if.else6
+
+if.then5:
+  store i32 0, ptr %x, align 4
+  br label %if.end7
+
+if.else6:
+  store i32 2, ptr %x, align 4
+  br label %if.end7
+
+if.end7:
+  br label %if.end8
+
+if.end8:
+  br label %sw.epilog
+
+sw.bb9:
+  call void @h()
+  br label %sw.epilog
+
+sw.bb10:
+  call void @h()
+  br label %sw.epilog
+
+sw.bb11:
+  call void @j()
+  br label %sw.bb12
+
+sw.bb12:
+  call void @k()
+  br label %if.end15
+
+sw.epilog:
+  %5 = load i32, ptr %x, align 4
+  %cmp13 = icmp eq i32 %5, 0
+  br i1 %cmp13, label %if.then14, label %if.end15
+
+if.then14:
+  store i32 1, ptr %x, align 4
+  br label %if.end15
+
+if.end15:
+  ret void
+}
+
+declare void @h() #2
+
+declare void @j() #2
+
+declare void @k() #2
\ No newline at end of file

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

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

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.

This seems to duplicate existing functionality.

I think the function properties analysis already does everything that this pass does (you can view results on IR by using the print<func-properties> pass with opt).

There's also the InstCount pass that counts individual instruction types and exposes them as statistics.

@github-actions
Copy link

github-actions bot commented Dec 8, 2025

🐧 Linux x64 Test Results

  • 166880 tests passed
  • 2918 tests skipped
  • 1 test failed

Failed Tests

(click on a test name to see its output)

LLVM

LLVM.Other/new-pm-defaults.ll
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 10
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/opt -disable-verify -verify-analysis-invalidation=0 -eagerly-invalidate-analyses=0 -debug-pass-manager      -passes='default<O1>' -S /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Other/new-pm-defaults.ll 2>&1      | /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Other/new-pm-defaults.ll --check-prefixes=CHECK-O,CHECK-DEFAULT,CHECK-O1,CHECK-NOEXT
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/opt -disable-verify -verify-analysis-invalidation=0 -eagerly-invalidate-analyses=0 -debug-pass-manager '-passes=default<O1>' -S /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Other/new-pm-defaults.ll
# note: command had no output on stdout or stderr
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Other/new-pm-defaults.ll --check-prefixes=CHECK-O,CHECK-DEFAULT,CHECK-O1,CHECK-NOEXT
# .---command stderr------------
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Other/new-pm-defaults.ll:302:17: error: CHECK-O-NEXT: is not on the line after the previous match
# | ; CHECK-O-NEXT: Running pass: PrintModulePass
# |                 ^
# | <stdin>:141:1: note: 'next' match was here
# | Running pass: PrintModulePass on [module]
# | ^
# | <stdin>:139:43: note: previous match ended here
# | Running pass: AnnotationRemarksPass on foo (7 instructions)
# |                                           ^
# | <stdin>:140:1: note: non-matching line after previous match is here
# | Running pass: CountInstructionsPass on foo (7 instructions)
# | ^
# | 
# | Input file: <stdin>
# | Check file: /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Other/new-pm-defaults.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |           .
# |           .
# |           .
# |         136: Running pass: ConstantMergePass on [module] 
# |         137: Running pass: CGProfilePass on [module] 
# |         138: Running pass: RelLookupTableConverterPass on [module] 
# |         139: Running pass: AnnotationRemarksPass on foo (7 instructions) 
# |         140: Running pass: CountInstructionsPass on foo (7 instructions) 
# |         141: Running pass: PrintModulePass on [module] 
# | next:302     !~~~~~~~~~~~~~~~~~~~~~~~~~~~~              error: match on wrong line
# |         142: ; ModuleID = '/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Other/new-pm-defaults.ll' 
# |         143: source_filename = "/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Other/new-pm-defaults.ll" 
# |         144:  
# |         145: declare void @bar() local_unnamed_addr 
# |         146:  
# |           .
# |           .
# |           .
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

@aeubanks
Copy link
Contributor

aeubanks commented Dec 8, 2025

using InstCount makes sense, and adding it to the opt pipeline when stats are turned on would be nice, similar to

FPM.addPass(CountVisitsPass());

@InakiVA InakiVA closed this Dec 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants