https://github.com/jofrn updated 
https://github.com/llvm/llvm-project/pull/199391

>From 22d9a5d9e35be5a1ec21278aad4d979ac2acaa60 Mon Sep 17 00:00:00 2001
From: jofrn <[email protected]>
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 <[email protected]>
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
 #

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to