[llvm] CallSiteSplitting: Replace -callsite-splitting-duplication-threshold cl::opt with pass parameter (PR #223479)

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 14 10:57:29 PDT 2026


https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/223479

Migrate the pass-specific command-line option to a new pass manager
pass parameter, exposed as callsite-splitting<duplication-threshold=N>.

Co-authored-by: Claude (Opus 4.8) <noreply at anthropic.com>

>From 70235c38a8e1cca0f7e0a9fda90a17a25965f8a6 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Mon, 14 Sep 2026 16:48:30 +0200
Subject: [PATCH] CallSiteSplitting: Replace
 -callsite-splitting-duplication-threshold cl::opt with pass parameter

Migrate the pass-specific command-line option to a new pass manager
pass parameter, exposed as callsite-splitting<duplication-threshold=N>.

Co-authored-by: Claude (Opus 4.8) <noreply at anthropic.com>
---
 .../Transforms/Scalar/CallSiteSplitting.h     | 15 +++++++-
 llvm/lib/Passes/PassBuilder.cpp               | 24 +++++++++++++
 llvm/lib/Passes/PassRegistry.def              |  7 +++-
 .../Transforms/Scalar/CallSiteSplitting.cpp   | 34 ++++++++++---------
 llvm/test/Other/new-pm-print-pipeline.ll      |  3 ++
 .../callsite-instructions-before-call.ll      |  2 +-
 .../CallSiteSplitting/convergent.ll           |  2 +-
 .../CallSiteSplitting/noduplicate.ll          |  2 +-
 .../pass-param-parse-errors.ll                |  7 ++++
 9 files changed, 75 insertions(+), 21 deletions(-)
 create mode 100644 llvm/test/Transforms/CallSiteSplitting/pass-param-parse-errors.ll

diff --git a/llvm/include/llvm/Transforms/Scalar/CallSiteSplitting.h b/llvm/include/llvm/Transforms/Scalar/CallSiteSplitting.h
index d1c47614656c5..56d318011dc26 100644
--- a/llvm/include/llvm/Transforms/Scalar/CallSiteSplitting.h
+++ b/llvm/include/llvm/Transforms/Scalar/CallSiteSplitting.h
@@ -15,9 +15,22 @@ namespace llvm {
 
 class Function;
 
-struct CallSiteSplittingPass : OptionalPassInfoMixin<CallSiteSplittingPass> {
+class CallSiteSplittingPass
+    : public OptionalPassInfoMixin<CallSiteSplittingPass> {
+  /// Only allow instructions before a call, if their cost is below
+  /// DuplicationThreshold.
+  const unsigned DuplicationThreshold;
+
+public:
+  CallSiteSplittingPass(unsigned DuplicationThreshold = 5)
+      : DuplicationThreshold(DuplicationThreshold) {}
+
   /// Run the pass over the function.
   LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+
+  LLVM_ABI void
+  printPipeline(raw_ostream &OS,
+                function_ref<StringRef(StringRef)> MapClassName2PassName);
 };
 } // end namespace llvm
 
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 725ce2d589a31..1a72515fe4025 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -898,6 +898,30 @@ Expected<bool> parseInstCountOptions(StringRef Params) {
   return PassBuilder::parseSinglePassOption(Params, "pre-opt", "InstCountPass");
 }
 
+Expected<unsigned> parseCallSiteSplittingOptions(StringRef Params) {
+  unsigned DuplicationThreshold = 5;
+  while (!Params.empty()) {
+    StringRef ParamName;
+    std::tie(ParamName, Params) = Params.split(';');
+
+    if (ParamName.consume_front("duplication-threshold=")) {
+      if (ParamName.getAsInteger(0, DuplicationThreshold))
+        return make_error<StringError>(
+            formatv("invalid argument to CallSiteSplitting pass "
+                    "duplication-threshold parameter: '{}'",
+                    ParamName)
+                .str(),
+            inconvertibleErrorCode());
+    } else {
+      return make_error<StringError>(
+          formatv("invalid CallSiteSplitting pass parameter '{}'", ParamName)
+              .str(),
+          inconvertibleErrorCode());
+    }
+  }
+  return DuplicationThreshold;
+}
+
 /// Parser of parameters for LoopUnroll pass.
 Expected<LoopUnrollOptions> parseLoopUnrollOptions(StringRef Params) {
   LoopUnrollOptions UnrollOpts;
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 43a15f4cf9ff3..ee786955d440a 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -417,7 +417,6 @@ FUNCTION_PASS("assume-simplify", AssumeSimplifyPass())
 FUNCTION_PASS("atomic-expand", AtomicExpandPass(*TM))
 FUNCTION_PASS("bdce", BDCEPass())
 FUNCTION_PASS("break-crit-edges", BreakCriticalEdgesPass())
-FUNCTION_PASS("callsite-splitting", CallSiteSplittingPass())
 FUNCTION_PASS("cfguard", CFGuardPass())
 FUNCTION_PASS("chr", ControlHeightReductionPass())
 FUNCTION_PASS("codegenprepare", CodeGenPreparePass(*TM))
@@ -581,6 +580,12 @@ FUNCTION_PASS("wasm-eh-prepare", WasmEHPreparePass())
 #ifndef FUNCTION_PASS_WITH_PARAMS
 #define FUNCTION_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS)
 #endif
+FUNCTION_PASS_WITH_PARAMS(
+    "callsite-splitting", "CallSiteSplittingPass",
+    [](unsigned DuplicationThreshold) {
+      return CallSiteSplittingPass(DuplicationThreshold);
+    },
+    parseCallSiteSplittingOptions, "duplication-threshold=N")
 FUNCTION_PASS_WITH_PARAMS(
     "early-cse", "EarlyCSEPass",
     [](bool UseMemorySSA) { return EarlyCSEPass(UseMemorySSA); },
diff --git a/llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp b/llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp
index ce73c44959a0e..12efd05e01952 100644
--- a/llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp
+++ b/llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp
@@ -62,7 +62,6 @@
 #include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/PatternMatch.h"
-#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Transforms/Utils/Cloning.h"
 #include "llvm/Transforms/Utils/Local.h"
@@ -74,15 +73,6 @@ using namespace PatternMatch;
 
 STATISTIC(NumCallSiteSplit, "Number of call-site split");
 
-/// Only allow instructions before a call, if their CodeSize cost is below
-/// DuplicationThreshold. Those instructions need to be duplicated in all
-/// split blocks.
-static cl::opt<unsigned>
-    DuplicationThreshold("callsite-splitting-duplication-threshold", cl::Hidden,
-                         cl::desc("Only allow instructions before a call, if "
-                                  "their cost is below DuplicationThreshold"),
-                         cl::init(5));
-
 static void addNonNullAttribute(CallBase &CB, Value *Op) {
   unsigned ArgNo = 0;
   for (auto &I : CB.args()) {
@@ -181,7 +171,8 @@ static SmallVector<BasicBlock *, 2> getTwoPredecessors(BasicBlock *BB) {
   return Preds;
 }
 
-static bool canSplitCallSite(CallBase &CB, TargetTransformInfo &TTI) {
+static bool canSplitCallSite(CallBase &CB, TargetTransformInfo &TTI,
+                             unsigned DuplicationThreshold) {
   if (CB.isConvergent() || CB.cannotDuplicate())
     return false;
 
@@ -485,9 +476,10 @@ static PredsWithCondsTy shouldSplitOnPredicatedArgument(CallBase &CB,
 }
 
 static bool tryToSplitCallSite(CallBase &CB, TargetTransformInfo &TTI,
-                               DomTreeUpdater &DTU) {
+                               DomTreeUpdater &DTU,
+                               unsigned DuplicationThreshold) {
   // Check if we can split the call site.
-  if (!CB.arg_size() || !canSplitCallSite(CB, TTI))
+  if (!CB.arg_size() || !canSplitCallSite(CB, TTI, DuplicationThreshold))
     return false;
 
   auto PredsWithConds = shouldSplitOnPredicatedArgument(CB, DTU);
@@ -501,7 +493,8 @@ static bool tryToSplitCallSite(CallBase &CB, TargetTransformInfo &TTI,
 }
 
 static bool doCallSiteSplitting(Function &F, TargetLibraryInfo &TLI,
-                                TargetTransformInfo &TTI, DominatorTree &DT) {
+                                TargetTransformInfo &TTI, DominatorTree &DT,
+                                unsigned DuplicationThreshold) {
 
   DomTreeUpdater DTU(&DT, DomTreeUpdater::UpdateStrategy::Lazy);
   bool Changed = false;
@@ -525,7 +518,7 @@ static bool doCallSiteSplitting(Function &F, TargetLibraryInfo &TLI,
       // Check if such path is possible before attempting the splitting.
       bool IsMustTail = CB->isMustTailCall();
 
-      Changed |= tryToSplitCallSite(*CB, TTI, DTU);
+      Changed |= tryToSplitCallSite(*CB, TTI, DTU, DuplicationThreshold);
 
       // There're no interesting instructions after this. The call site
       // itself might have been erased on splitting.
@@ -542,9 +535,18 @@ PreservedAnalyses CallSiteSplittingPass::run(Function &F,
   auto &TTI = AM.getResult<TargetIRAnalysis>(F);
   auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
 
-  if (!doCallSiteSplitting(F, TLI, TTI, DT))
+  if (!doCallSiteSplitting(F, TLI, TTI, DT, DuplicationThreshold))
     return PreservedAnalyses::all();
   PreservedAnalyses PA;
   PA.preserve<DominatorTreeAnalysis>();
   return PA;
 }
+
+void CallSiteSplittingPass::printPipeline(
+    raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
+  static_cast<PassInfoMixin<CallSiteSplittingPass> *>(this)->printPipeline(
+      OS, MapClassName2PassName);
+  OS << '<';
+  OS << "duplication-threshold=" << DuplicationThreshold;
+  OS << '>';
+}
diff --git a/llvm/test/Other/new-pm-print-pipeline.ll b/llvm/test/Other/new-pm-print-pipeline.ll
index 2a1192b6b66bd..386be8ab90b40 100644
--- a/llvm/test/Other/new-pm-print-pipeline.ll
+++ b/llvm/test/Other/new-pm-print-pipeline.ll
@@ -123,3 +123,6 @@
 
 ; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='drop-unnecessary-assumes,drop-unnecessary-assumes<drop-deref>' < %s | FileCheck %s --check-prefixes=CHECK-38
 ; CHECK-38: drop-unnecessary-assumes,drop-unnecessary-assumes<drop-deref>
+
+; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(callsite-splitting,callsite-splitting<duplication-threshold=10>)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-39
+; CHECK-39: function(callsite-splitting<duplication-threshold=5>,callsite-splitting<duplication-threshold=10>)
diff --git a/llvm/test/Transforms/CallSiteSplitting/callsite-instructions-before-call.ll b/llvm/test/Transforms/CallSiteSplitting/callsite-instructions-before-call.ll
index 162751a1ce324..f6aada6d4aa06 100644
--- a/llvm/test/Transforms/CallSiteSplitting/callsite-instructions-before-call.ll
+++ b/llvm/test/Transforms/CallSiteSplitting/callsite-instructions-before-call.ll
@@ -1,5 +1,5 @@
 ; RUN: opt -S -passes=callsite-splitting < %s | FileCheck --check-prefix=CHECK %s
-; RUN: opt -S -passes=callsite-splitting -callsite-splitting-duplication-threshold=0 < %s | FileCheck --check-prefix=NODUP %s
+; RUN: opt -S -passes='callsite-splitting<duplication-threshold=0>' < %s | FileCheck --check-prefix=NODUP %s
 
 ; Instructions before a call that will be pushed to its predecessors
 ; with uses after the callsite, must be patched up as PHI nodes in
diff --git a/llvm/test/Transforms/CallSiteSplitting/convergent.ll b/llvm/test/Transforms/CallSiteSplitting/convergent.ll
index 934f7e86157bd..447c3826f7be6 100644
--- a/llvm/test/Transforms/CallSiteSplitting/convergent.ll
+++ b/llvm/test/Transforms/CallSiteSplitting/convergent.ll
@@ -1,4 +1,4 @@
-; RUN: opt -S -passes=callsite-splitting -callsite-splitting-duplication-threshold=100000000 < %s | FileCheck -enable-var-scope %s
+; RUN: opt -S -passes='callsite-splitting<duplication-threshold=100000000>' < %s | FileCheck -enable-var-scope %s
 
 ; Convergent calls should not be duplicated in this case
 ; CHECK-LABEL: define void @convergent_caller(
diff --git a/llvm/test/Transforms/CallSiteSplitting/noduplicate.ll b/llvm/test/Transforms/CallSiteSplitting/noduplicate.ll
index bdec9eb63e408..e9078bf76f1dd 100644
--- a/llvm/test/Transforms/CallSiteSplitting/noduplicate.ll
+++ b/llvm/test/Transforms/CallSiteSplitting/noduplicate.ll
@@ -1,4 +1,4 @@
-; RUN: opt -S -passes=callsite-splitting -callsite-splitting-duplication-threshold=100000000 < %s | FileCheck -enable-var-scope %s
+; RUN: opt -S -passes='callsite-splitting<duplication-threshold=100000000>' < %s | FileCheck -enable-var-scope %s
 
 ; Noduplicate calls should not be duplicated
 ; CHECK-LABEL: define void @noduplicate_caller(
diff --git a/llvm/test/Transforms/CallSiteSplitting/pass-param-parse-errors.ll b/llvm/test/Transforms/CallSiteSplitting/pass-param-parse-errors.ll
new file mode 100644
index 0000000000000..8c5a827f7bdb4
--- /dev/null
+++ b/llvm/test/Transforms/CallSiteSplitting/pass-param-parse-errors.ll
@@ -0,0 +1,7 @@
+; RUN: not opt -passes='callsite-splitting<unknown>' -disable-output %s 2>&1 | FileCheck -check-prefix=UNKNOWNERR %s
+; RUN: not opt -passes='callsite-splitting<duplication-threshold=>' -disable-output %s 2>&1 | FileCheck -check-prefix=DUP-EMPTY-ERR %s
+; RUN: not opt -passes='callsite-splitting<duplication-threshold=x>' -disable-output %s 2>&1 | FileCheck -check-prefix=DUP-NOTINT-ERR %s
+
+; UNKNOWNERR: invalid CallSiteSplitting pass parameter '{{.*}}'
+; DUP-EMPTY-ERR: invalid argument to CallSiteSplitting pass duplication-threshold parameter: ''
+; DUP-NOTINT-ERR: invalid argument to CallSiteSplitting pass duplication-threshold parameter: 'x'



More information about the llvm-commits mailing list