refactor to move function-selection from lit core into llvm/test config
diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py
index 09df1e3..0cef6ff 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 acc8c52..4be2a0f 100644
--- a/llvm/utils/lit/lit/LitConfig.py
+++ b/llvm/utils/lit/lit/LitConfig.py
@@ -42,7 +42,6 @@
         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 @@
         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 cc7c9384..82852f1 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -1921,23 +1921,6 @@
         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 @@
         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 fd8e9f5e..bebde4b 100644
--- a/llvm/utils/lit/lit/cl_arguments.py
+++ b/llvm/utils/lit/lit/cl_arguments.py
@@ -428,16 +428,6 @@
         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",
         type=_case_insensitive_regex,
@@ -597,13 +587,6 @@
     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 0000000..e994749
--- /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 2dcd0dc..77b23bf 100755
--- a/llvm/utils/lit/lit/main.py
+++ b/llvm/utils/lit/lit/main.py
@@ -44,7 +44,6 @@
         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 3f70040..2298433 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 2a11cb7..68237ae 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
 #