[llvm] [lit] Add --fn to prepend llvm-extract for function-level test narrowing (PR #199814)
via llvm-commits
llvm-commits at lists.llvm.org
Tue May 26 20:09:34 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-testing-tools
Author: jofrn
<details>
<summary>Changes</summary>
Add a --fn=name1,name2 flag to llvm-lit that prepends
llvm-extract --func=<name> to the first pipeline stage of each
RUN line whose first stage references %s. This lets users narrow
IR tests to specific functions and their dependencies without
modifying test files.
---
Full diff: https://github.com/llvm/llvm-project/pull/199814.diff
8 Files Affected:
- (modified) llvm/utils/lit/lit/LitConfig.py (+2)
- (modified) llvm/utils/lit/lit/TestRunner.py (+30)
- (modified) llvm/utils/lit/lit/cl_arguments.py (+17)
- (modified) llvm/utils/lit/lit/main.py (+1)
- (added) llvm/utils/lit/tests/Inputs/fn-selection/lit.cfg (+11)
- (added) llvm/utils/lit/tests/Inputs/fn-selection/mock-bin/llvm-extract (+2)
- (added) llvm/utils/lit/tests/Inputs/fn-selection/sample.ll (+2)
- (added) llvm/utils/lit/tests/fn-selection.py (+25)
``````````diff
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..7254acc322de6 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -1921,6 +1921,34 @@ def _replaceReadFile(match):
commandLine = "%s && test -e %s" % (commandLine, filePath)
return commandLine
+
+def _applyFnSelection(script, fn_names):
+ """Prepend llvm-extract to each command so the pipeline receives reduced IR."""
+ if not fn_names:
+ return script
+ func_args = " ".join("--func=" + n for n in fn_names)
+ extract = "llvm-extract " + func_args + " %s -o -"
+ for directive in script:
+ if not isinstance(directive, CommandDirective):
+ continue
+ cmd = directive.command
+ pipe_idx = cmd.find("|")
+ first, rest = (cmd[:pipe_idx], cmd[pipe_idx:]) if pipe_idx != -1 else (cmd, "")
+ if "%s" not in first:
+ continue
+ # Preserve %dbg(...) marker at the start of the command
+ dbg = re.match(r"(%dbg\([^)]*\))\s*", first)
+ prefix = dbg.group(1) + " " if dbg else ""
+ if dbg:
+ first = first[dbg.end():]
+ # Strip '< %s' stdin redirect so the tool reads from the pipe instead
+ first = re.sub(r"<\s*%s(?=\s|$)", "", first)
+ # Strip '%s' positional arg (only when whitespace-delimited)
+ first = re.sub(r"(?<!\S)%s(?=\s|$)", "", first)
+ directive.command = prefix + extract + " | " + first.strip() + " " + rest
+ return script
+
+
def executeShTest(
test, litConfig, useExternalSh, extra_substitutions=[], preamble_commands=[]
):
@@ -1938,6 +1966,8 @@ def executeShTest(
if litConfig.noExecute:
return lit.Test.Result(Test.PASS)
+ script = _applyFnSelection(script, litConfig.fnSelection)
+
tmpDir, tmpBase = getTempPaths(test)
substitutions = list(extra_substitutions)
substitutions += getDefaultSubstitutions(
diff --git a/llvm/utils/lit/lit/cl_arguments.py b/llvm/utils/lit/lit/cl_arguments.py
index bebde4b762b0e..926b1bb417220 100644
--- a/llvm/utils/lit/lit/cl_arguments.py
+++ b/llvm/utils/lit/lit/cl_arguments.py
@@ -481,6 +481,16 @@ def parse_args():
default=os.environ.get("LIT_RUN_SHARD"),
)
+ selection_group.add_argument(
+ "--fn",
+ dest="fnSelection",
+ metavar="NAMES",
+ type=_comma_list,
+ default=None,
+ help="Pipe IR output through llvm-extract to keep only the named "
+ "functions (comma-separated) and their dependencies",
+ )
+
debug_group = parser.add_argument_group("Debug and Experimental Options")
debug_group.add_argument(
"--debug", help="Enable debugging (for 'lit' development)", action="store_true"
@@ -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..23a704af5ad93
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/fn-selection/lit.cfg
@@ -0,0 +1,11 @@
+import lit.formats
+config.name = "fn-selection"
+config.suffixes = [".ll"]
+config.test_format = lit.formats.ShTest()
+config.test_source_root = os.path.dirname(__file__)
+config.test_exec_root = config.test_source_root
+config.environment["PATH"] = (
+ os.path.join(config.test_source_root, "mock-bin")
+ + os.pathsep
+ + config.environment.get("PATH", "")
+)
diff --git a/llvm/utils/lit/tests/Inputs/fn-selection/mock-bin/llvm-extract b/llvm/utils/lit/tests/Inputs/fn-selection/mock-bin/llvm-extract
new file mode 100755
index 0000000000000..039e4d0069c5c
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/fn-selection/mock-bin/llvm-extract
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 0
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..d1c6ec435d53e
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/fn-selection/sample.ll
@@ -0,0 +1,2 @@
+; RUN: echo opt %s -S -passes='instcombine' | echo FileCheck %s
+; RUN: echo opt < %s -S -passes="instcombine" | echo FileCheck %s
diff --git a/llvm/utils/lit/tests/fn-selection.py b/llvm/utils/lit/tests/fn-selection.py
new file mode 100644
index 0000000000000..0364592efe649
--- /dev/null
+++ b/llvm/utils/lit/tests/fn-selection.py
@@ -0,0 +1,25 @@
+# Verify lit's --fn flag prepends llvm-extract to pipelines.
+
+# --- --fn=foo: single function ---
+# RUN: %{lit} -a --fn=foo %{inputs}/fn-selection/sample.ll \
+# RUN: | FileCheck --check-prefix=SINGLE %s
+#
+# Positional %s form:
+# SINGLE: llvm-extract --func=foo {{.*}}sample.ll -o - | echo opt -S -passes='instcombine' | echo FileCheck {{.*}}sample.ll
+# Redirect < %s form:
+# SINGLE: llvm-extract --func=foo {{.*}}sample.ll -o - | echo opt -S -passes="instcombine" | echo FileCheck {{.*}}sample.ll
+
+# --- --fn=foo,bar: multiple functions ---
+# RUN: %{lit} -a --fn=foo,bar %{inputs}/fn-selection/sample.ll \
+# RUN: | FileCheck --check-prefix=MULTI %s
+#
+# MULTI: llvm-extract --func=foo --func=bar {{.*}}sample.ll -o - | echo opt -S -passes='instcombine' | echo FileCheck {{.*}}sample.ll
+# MULTI: llvm-extract --func=foo --func=bar {{.*}}sample.ll -o - | echo opt -S -passes="instcombine" | echo FileCheck {{.*}}sample.ll
+
+# --- No --fn: passes unchanged ---
+# RUN: %{lit} -a %{inputs}/fn-selection/sample.ll \
+# RUN: | FileCheck --check-prefix=NONE %s
+#
+# NONE-NOT: llvm-extract
+# NONE: echo opt {{.*}}sample.ll -S -passes='instcombine'
+# NONE: echo opt < {{.*}}sample.ll -S -passes="instcombine"
``````````
</details>
https://github.com/llvm/llvm-project/pull/199814
More information about the llvm-commits
mailing list