[llvm-branch-commits] [llvm] Add --fn flag to llvm-lit to inject select-function pass into opt pipelines (PR #199391)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed May 27 20:47:24 PDT 2026
https://github.com/jofrn updated https://github.com/llvm/llvm-project/pull/199391
>From 22d9a5d9e35be5a1ec21278aad4d979ac2acaa60 Mon Sep 17 00:00:00 2001
From: jofrn <jo7frn1 at gmail.com>
Date: Sat, 23 May 2026 18:16:31 -0700
Subject: [PATCH 1/2] Add --fn flag to llvm-lit to inject select-function pass
into opt pipelines
Translates --fn=fn0,fn1 into -passes='select-function<fn=fn0;fn=fn1>,...'
by rewriting -passes= arguments in RUN lines after substitution.
Handles both single and double quoted pass pipelines.
---
llvm/utils/lit/lit/LitConfig.py | 2 ++
llvm/utils/lit/lit/TestRunner.py | 19 +++++++++++++++
llvm/utils/lit/lit/cl_arguments.py | 17 ++++++++++++++
llvm/utils/lit/lit/main.py | 1 +
.../lit/tests/Inputs/fn-selection/lit.cfg | 7 ++++++
.../lit/tests/Inputs/fn-selection/sample.ll | 2 ++
llvm/utils/lit/tests/fn-selection.py | 23 +++++++++++++++++++
7 files changed, 71 insertions(+)
create mode 100644 llvm/utils/lit/tests/Inputs/fn-selection/lit.cfg
create mode 100644 llvm/utils/lit/tests/Inputs/fn-selection/sample.ll
create mode 100644 llvm/utils/lit/tests/fn-selection.py
diff --git a/llvm/utils/lit/lit/LitConfig.py b/llvm/utils/lit/lit/LitConfig.py
index 4be2a0f6d8121..acc8c52ea27c8 100644
--- a/llvm/utils/lit/lit/LitConfig.py
+++ b/llvm/utils/lit/lit/LitConfig.py
@@ -42,6 +42,7 @@ def __init__(
per_test_coverage=False,
gtest_sharding=True,
update_tests=False,
+ fnSelection=None,
):
# The name of the test runner.
self.progname = progname
@@ -96,6 +97,7 @@ def __init__(
self.gtest_sharding = bool(gtest_sharding)
self.update_tests = update_tests
self.test_updaters = [diff_test_updater]
+ self.fnSelection = fnSelection
@property
def maxIndividualTestTime(self):
diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index 82852f1852705..cc7c93848f09e 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -1921,6 +1921,23 @@ def _replaceReadFile(match):
commandLine = "%s && test -e %s" % (commandLine, filePath)
return commandLine
+
+def _applyFnSelection(script, fn_names):
+ """Inject select-function pass into opt -passes= pipelines."""
+ if not fn_names:
+ return script
+ fn_args = ";".join("fn=" + n for n in fn_names)
+ sel = "select-function<" + fn_args + ">"
+ out = []
+ for cmd in script:
+ # -passes='...' or -passes="..."
+ cmd = re.sub(r"""-passes=(['"])""", r"-passes=\1" + sel + ",", cmd)
+ # -passes=word (unquoted) — wrap in quotes to protect angle brackets
+ cmd = re.sub(r"-passes=([^'\"\s]\S*)", r"-passes='" + sel + r",\1'", cmd)
+ out.append(cmd)
+ return out
+
+
def executeShTest(
test, litConfig, useExternalSh, extra_substitutions=[], preamble_commands=[]
):
@@ -1955,6 +1972,8 @@ def executeShTest(
recursion_limit=test.config.recursiveExpansionLimit,
)
+ script = _applyFnSelection(script, litConfig.fnSelection)
+
if useExternalSh:
for index, command in enumerate(script):
script[index] = _expandLateSubstitutionsExternal(command)
diff --git a/llvm/utils/lit/lit/cl_arguments.py b/llvm/utils/lit/lit/cl_arguments.py
index bebde4b762b0e..fd8e9f5e26211 100644
--- a/llvm/utils/lit/lit/cl_arguments.py
+++ b/llvm/utils/lit/lit/cl_arguments.py
@@ -427,6 +427,16 @@ def parse_args():
help="Only run tests with paths matching the given regular expression",
default=os.environ.get("LIT_FILTER", ".*"),
)
+ selection_group.add_argument(
+ "--fn",
+ dest="fnSelection",
+ metavar="NAMES",
+ type=_comma_list,
+ default=None,
+ help="Inject select-function pass into opt commands so only the "
+ "named functions (and their dependencies) are compiled. "
+ "NAMES is a comma-separated list of function names.",
+ )
selection_group.add_argument(
"--filter-out",
metavar="REGEX",
@@ -587,6 +597,13 @@ def _semicolon_list(arg):
return arg.split(";")
+def _comma_list(arg):
+ names = [n.strip() for n in arg.split(",") if n.strip()]
+ if not names:
+ raise _error("empty function name list")
+ return names
+
+
def _error(desc, *args):
msg = desc.format(*args)
return argparse.ArgumentTypeError(msg)
diff --git a/llvm/utils/lit/lit/main.py b/llvm/utils/lit/lit/main.py
index 77b23bf560c6e..2dcd0dc328957 100755
--- a/llvm/utils/lit/lit/main.py
+++ b/llvm/utils/lit/lit/main.py
@@ -44,6 +44,7 @@ def main(builtin_params={}):
gtest_sharding=opts.gtest_sharding,
maxRetriesPerTest=opts.maxRetriesPerTest,
update_tests=opts.update_tests,
+ fnSelection=opts.fnSelection,
)
discovered_tests = lit.discovery.find_tests_for_inputs(
diff --git a/llvm/utils/lit/tests/Inputs/fn-selection/lit.cfg b/llvm/utils/lit/tests/Inputs/fn-selection/lit.cfg
new file mode 100644
index 0000000000000..3f700409586fc
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/fn-selection/lit.cfg
@@ -0,0 +1,7 @@
+import lit.formats
+
+config.name = "fn-selection"
+config.suffixes = [".ll"]
+config.test_format = lit.formats.ShTest()
+config.test_source_root = None
+config.test_exec_root = None
diff --git a/llvm/utils/lit/tests/Inputs/fn-selection/sample.ll b/llvm/utils/lit/tests/Inputs/fn-selection/sample.ll
new file mode 100644
index 0000000000000..30c01a9469750
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/fn-selection/sample.ll
@@ -0,0 +1,2 @@
+; RUN: echo -passes='instcombine,mem2reg'
+; RUN: echo -passes="instcombine,mem2reg"
diff --git a/llvm/utils/lit/tests/fn-selection.py b/llvm/utils/lit/tests/fn-selection.py
new file mode 100644
index 0000000000000..2a11cb7e15bba
--- /dev/null
+++ b/llvm/utils/lit/tests/fn-selection.py
@@ -0,0 +1,23 @@
+# Verify lit's --fn flag injects the select-function pass into -passes= args.
+
+# --- --fn=foo: single function ---
+# RUN: %{lit} -a --fn=foo %{inputs}/fn-selection/sample.ll \
+# RUN: | FileCheck --check-prefix=SINGLE %s
+#
+# SINGLE: -passes='select-function<fn=foo>,instcombine,mem2reg'
+# SINGLE: -passes="select-function<fn=foo>,instcombine,mem2reg"
+
+# --- --fn=foo,bar: multiple functions ---
+# RUN: %{lit} -a --fn=foo,bar %{inputs}/fn-selection/sample.ll \
+# RUN: | FileCheck --check-prefix=MULTI %s
+#
+# MULTI: -passes='select-function<fn=foo;fn=bar>,instcombine,mem2reg'
+# MULTI: -passes="select-function<fn=foo;fn=bar>,instcombine,mem2reg"
+
+# --- No --fn: passes unchanged ---
+# RUN: %{lit} -a %{inputs}/fn-selection/sample.ll \
+# RUN: | FileCheck --check-prefix=NONE %s
+#
+# NONE-NOT: select-function
+# NONE: -passes='instcombine,mem2reg'
+# NONE: -passes="instcombine,mem2reg"
>From e7101217a21d1e3815bbd5e9654ad42c3a759eaf Mon Sep 17 00:00:00 2001
From: jofrn <165626406+jofrn at users.noreply.github.com>
Date: Wed, 27 May 2026 20:37:51 -0700
Subject: [PATCH 2/2] refactor to move function-selection from lit core into
llvm/test config
---
llvm/test/lit.cfg.py | 4 ++++
llvm/utils/lit/lit/LitConfig.py | 2 --
llvm/utils/lit/lit/TestRunner.py | 19 ---------------
llvm/utils/lit/lit/cl_arguments.py | 17 --------------
llvm/utils/lit/lit/llvm/fn_selection.py | 23 +++++++++++++++++++
llvm/utils/lit/lit/main.py | 1 -
.../lit/tests/Inputs/fn-selection/lit.cfg | 3 +++
llvm/utils/lit/tests/fn-selection.py | 13 ++++++-----
8 files changed, 37 insertions(+), 45 deletions(-)
create mode 100644 llvm/utils/lit/lit/llvm/fn_selection.py
diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py
index 09df1e3fd6281..0cef6ff30191c 100644
--- a/llvm/test/lit.cfg.py
+++ b/llvm/test/lit.cfg.py
@@ -39,6 +39,10 @@
)
config.test_format = lit.formats.ShTest(not use_lit_shell, extra_substitutions)
+from lit.llvm import fn_selection
+
+fn_selection.install(config, lit_config)
+
# suffixes: A list of file extensions to treat as test files. This is overriden
# by individual lit.local.cfg files in the test subdirectories.
config.suffixes = [".ll", ".c", ".test", ".txt", ".s", ".mir", ".yaml", ".spv"]
diff --git a/llvm/utils/lit/lit/LitConfig.py b/llvm/utils/lit/lit/LitConfig.py
index acc8c52ea27c8..4be2a0f6d8121 100644
--- a/llvm/utils/lit/lit/LitConfig.py
+++ b/llvm/utils/lit/lit/LitConfig.py
@@ -42,7 +42,6 @@ def __init__(
per_test_coverage=False,
gtest_sharding=True,
update_tests=False,
- fnSelection=None,
):
# The name of the test runner.
self.progname = progname
@@ -97,7 +96,6 @@ def __init__(
self.gtest_sharding = bool(gtest_sharding)
self.update_tests = update_tests
self.test_updaters = [diff_test_updater]
- self.fnSelection = fnSelection
@property
def maxIndividualTestTime(self):
diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index cc7c93848f09e..82852f1852705 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -1921,23 +1921,6 @@ def _replaceReadFile(match):
commandLine = "%s && test -e %s" % (commandLine, filePath)
return commandLine
-
-def _applyFnSelection(script, fn_names):
- """Inject select-function pass into opt -passes= pipelines."""
- if not fn_names:
- return script
- fn_args = ";".join("fn=" + n for n in fn_names)
- sel = "select-function<" + fn_args + ">"
- out = []
- for cmd in script:
- # -passes='...' or -passes="..."
- cmd = re.sub(r"""-passes=(['"])""", r"-passes=\1" + sel + ",", cmd)
- # -passes=word (unquoted) — wrap in quotes to protect angle brackets
- cmd = re.sub(r"-passes=([^'\"\s]\S*)", r"-passes='" + sel + r",\1'", cmd)
- out.append(cmd)
- return out
-
-
def executeShTest(
test, litConfig, useExternalSh, extra_substitutions=[], preamble_commands=[]
):
@@ -1972,8 +1955,6 @@ def executeShTest(
recursion_limit=test.config.recursiveExpansionLimit,
)
- script = _applyFnSelection(script, litConfig.fnSelection)
-
if useExternalSh:
for index, command in enumerate(script):
script[index] = _expandLateSubstitutionsExternal(command)
diff --git a/llvm/utils/lit/lit/cl_arguments.py b/llvm/utils/lit/lit/cl_arguments.py
index fd8e9f5e26211..bebde4b762b0e 100644
--- a/llvm/utils/lit/lit/cl_arguments.py
+++ b/llvm/utils/lit/lit/cl_arguments.py
@@ -427,16 +427,6 @@ def parse_args():
help="Only run tests with paths matching the given regular expression",
default=os.environ.get("LIT_FILTER", ".*"),
)
- selection_group.add_argument(
- "--fn",
- dest="fnSelection",
- metavar="NAMES",
- type=_comma_list,
- default=None,
- help="Inject select-function pass into opt commands so only the "
- "named functions (and their dependencies) are compiled. "
- "NAMES is a comma-separated list of function names.",
- )
selection_group.add_argument(
"--filter-out",
metavar="REGEX",
@@ -597,13 +587,6 @@ def _semicolon_list(arg):
return arg.split(";")
-def _comma_list(arg):
- names = [n.strip() for n in arg.split(",") if n.strip()]
- if not names:
- raise _error("empty function name list")
- return names
-
-
def _error(desc, *args):
msg = desc.format(*args)
return argparse.ArgumentTypeError(msg)
diff --git a/llvm/utils/lit/lit/llvm/fn_selection.py b/llvm/utils/lit/lit/llvm/fn_selection.py
new file mode 100644
index 0000000000000..e99474984f274
--- /dev/null
+++ b/llvm/utils/lit/lit/llvm/fn_selection.py
@@ -0,0 +1,23 @@
+"""Splice a `select-function<fn=...>` pass at the head of every `-passes=`
+pipeline so only the named functions (and their transitive dependencies) are
+compiled by `opt`. Driven by `--param fn=NAMES`."""
+
+from lit.TestingConfig import SubstituteCaptures
+
+
+def install(config, lit_config):
+ fn = lit_config.params.get("fn")
+ if not fn:
+ return
+ names = [n.strip() for n in fn.split(",") if n.strip()]
+ if not names:
+ return
+ sel = "select-function<" + ";".join("fn=" + n for n in names) + ">"
+ # -passes='...' / -passes="..." — splice select-function after the quote
+ config.substitutions.append(
+ (r"""-passes=(['"])""", SubstituteCaptures(r"-passes=\1" + sel + ","))
+ )
+ # -passes=word (unquoted) — wrap to protect angle brackets
+ config.substitutions.append(
+ (r"-passes=([^'\"\s]\S*)", SubstituteCaptures(r"-passes='" + sel + r",\1'"))
+ )
diff --git a/llvm/utils/lit/lit/main.py b/llvm/utils/lit/lit/main.py
index 2dcd0dc328957..77b23bf560c6e 100755
--- a/llvm/utils/lit/lit/main.py
+++ b/llvm/utils/lit/lit/main.py
@@ -44,7 +44,6 @@ def main(builtin_params={}):
gtest_sharding=opts.gtest_sharding,
maxRetriesPerTest=opts.maxRetriesPerTest,
update_tests=opts.update_tests,
- fnSelection=opts.fnSelection,
)
discovered_tests = lit.discovery.find_tests_for_inputs(
diff --git a/llvm/utils/lit/tests/Inputs/fn-selection/lit.cfg b/llvm/utils/lit/tests/Inputs/fn-selection/lit.cfg
index 3f700409586fc..2298433995293 100644
--- a/llvm/utils/lit/tests/Inputs/fn-selection/lit.cfg
+++ b/llvm/utils/lit/tests/Inputs/fn-selection/lit.cfg
@@ -1,7 +1,10 @@
import lit.formats
+from lit.llvm import fn_selection
config.name = "fn-selection"
config.suffixes = [".ll"]
config.test_format = lit.formats.ShTest()
config.test_source_root = None
config.test_exec_root = None
+
+fn_selection.install(config, lit_config)
diff --git a/llvm/utils/lit/tests/fn-selection.py b/llvm/utils/lit/tests/fn-selection.py
index 2a11cb7e15bba..68237ae016a56 100644
--- a/llvm/utils/lit/tests/fn-selection.py
+++ b/llvm/utils/lit/tests/fn-selection.py
@@ -1,20 +1,21 @@
-# Verify lit's --fn flag injects the select-function pass into -passes= args.
+# Verify --param fn=NAMES splices select-function into -passes= pipelines via
+# lit.llvm.fn_selection (which is also wired into llvm/test/lit.cfg.py).
-# --- --fn=foo: single function ---
-# RUN: %{lit} -a --fn=foo %{inputs}/fn-selection/sample.ll \
+# --- --param fn=foo: single function ---
+# RUN: %{lit} -a --param fn=foo %{inputs}/fn-selection/sample.ll \
# RUN: | FileCheck --check-prefix=SINGLE %s
#
# SINGLE: -passes='select-function<fn=foo>,instcombine,mem2reg'
# SINGLE: -passes="select-function<fn=foo>,instcombine,mem2reg"
-# --- --fn=foo,bar: multiple functions ---
-# RUN: %{lit} -a --fn=foo,bar %{inputs}/fn-selection/sample.ll \
+# --- --param fn=foo,bar: multiple functions ---
+# RUN: %{lit} -a --param fn=foo,bar %{inputs}/fn-selection/sample.ll \
# RUN: | FileCheck --check-prefix=MULTI %s
#
# MULTI: -passes='select-function<fn=foo;fn=bar>,instcombine,mem2reg'
# MULTI: -passes="select-function<fn=foo;fn=bar>,instcombine,mem2reg"
-# --- No --fn: passes unchanged ---
+# --- No --param: passes unchanged ---
# RUN: %{lit} -a %{inputs}/fn-selection/sample.ll \
# RUN: | FileCheck --check-prefix=NONE %s
#
More information about the llvm-branch-commits
mailing list