https://github.com/jofrn updated https://github.com/llvm/llvm-project/pull/200618
>From cefc58c17bd972f5f695f73fc1cf73bccbce30a0 Mon Sep 17 00:00:00 2001 From: jofrn <[email protected]> Date: Thu, 21 May 2026 15:01:13 -0700 Subject: [PATCH 1/2] [lit] Add --check to run only selected RUN lines from a test `llvm-lit --check=LIST <test>` keeps only the listed RUN directives in the test and discards the rest. LIST is a comma-separated mix of 0-indexed integers and ranges (e.g. `--check=0,2,4-6`). The selection is applied to the parseIntegratedTestScript output. Run tests via `llvm-lit --check=0 llvm/utils/lit/tests/Inputs/check-filter/sample.ll`, `llvm-lit --check=1 llvm/utils/lit/tests/Inputs/check-filter/sample.ll`, `llvm/utils/lit/lit.py llvm/utils/lit/tests/check-filter.py`. --- llvm/utils/lit/lit/LitConfig.py | 2 ++ llvm/utils/lit/lit/TestRunner.py | 18 ++++++++++ llvm/utils/lit/lit/cl_arguments.py | 36 +++++++++++++++++++ llvm/utils/lit/lit/main.py | 1 + .../lit/tests/Inputs/check-filter/lit.cfg | 7 ++++ .../lit/tests/Inputs/check-filter/sample.ll | 4 +++ llvm/utils/lit/tests/check-filter.py | 23 ++++++++++++ 7 files changed, 91 insertions(+) create mode 100644 llvm/utils/lit/tests/Inputs/check-filter/lit.cfg create mode 100644 llvm/utils/lit/tests/Inputs/check-filter/sample.ll create mode 100644 llvm/utils/lit/tests/check-filter.py diff --git a/llvm/utils/lit/lit/LitConfig.py b/llvm/utils/lit/lit/LitConfig.py index 4be2a0f6d8121..43af8ed989508 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, + checkFilter=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.checkFilter = checkFilter @property def maxIndividualTestTime(self): diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py index 82852f1852705..4a0a969b1d804 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 _applyCheckFilter(test, check_filter): + """Keep only CommandDirective entries whose 0-indexed integer(s) + is/are in check_filter. + """ + if check_filter is None: + return test + out = [] + n = -1 + for d in test: + if isinstance(d, CommandDirective): + n += 1 + if n not in check_filter: + continue + out.append(d) + return out + + def executeShTest( test, litConfig, useExternalSh, extra_substitutions=[], preamble_commands=[] ): @@ -1933,6 +1950,7 @@ def executeShTest( parsed = parseIntegratedTestScript(test, require_script=not script) if isinstance(parsed, lit.Test.Result): return parsed + parsed = _applyCheckFilter(parsed, litConfig.checkFilter) script += parsed if litConfig.noExecute: diff --git a/llvm/utils/lit/lit/cl_arguments.py b/llvm/utils/lit/lit/cl_arguments.py index bebde4b762b0e..209fd456d805e 100644 --- a/llvm/utils/lit/lit/cl_arguments.py +++ b/llvm/utils/lit/lit/cl_arguments.py @@ -427,6 +427,15 @@ def parse_args(): help="Only run tests with paths matching the given regular expression", default=os.environ.get("LIT_FILTER", ".*"), ) + selection_group.add_argument( + "--check", + dest="checkFilter", + metavar="LIST", + type=_check_filter, + default=None, + help="Within each test file, keep only the listed RUN lines. " + "LIST is comma-separated 0-indexed integers or ranges: '1,3-5'.", + ) selection_group.add_argument( "--filter-out", metavar="REGEX", @@ -587,6 +596,33 @@ def _semicolon_list(arg): return arg.split(";") +def _check_filter(arg): + out = [] + for tok in arg.split(","): + tok = tok.strip() + if not tok: + continue + if "-" in tok and tok[0] != "-": + a, sep, b = tok.partition("-") + if a.isdigit() and b.isdigit(): + lo, hi = int(a), int(b) + if hi < lo: + raise _error( + "range '{}' is empty (high < low)", tok + ) + out.extend(range(lo, hi + 1)) + continue + if tok.isdigit(): + out.append(int(tok)) + else: + raise _error( + "expected integer or integer range, got '{}'", tok + ) + if not out: + raise _error("empty selector list") + return out + + 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..ae2964e157fbc 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, + checkFilter=opts.checkFilter, ) discovered_tests = lit.discovery.find_tests_for_inputs( diff --git a/llvm/utils/lit/tests/Inputs/check-filter/lit.cfg b/llvm/utils/lit/tests/Inputs/check-filter/lit.cfg new file mode 100644 index 0000000000000..756e2d63e8fb0 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/check-filter/lit.cfg @@ -0,0 +1,7 @@ +import lit.formats + +config.name = "check-filter" +config.suffixes = [".ll", ".mir", ".s"] +config.test_format = lit.formats.ShTest() +config.test_source_root = None +config.test_exec_root = None diff --git a/llvm/utils/lit/tests/Inputs/check-filter/sample.ll b/llvm/utils/lit/tests/Inputs/check-filter/sample.ll new file mode 100644 index 0000000000000..27c686974a056 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/check-filter/sample.ll @@ -0,0 +1,4 @@ +; RUN: echo FIRST-RUN +; RUN: echo SECOND-RUN +define i32 @foo(i32 %a) { ret i32 0 } +define i32 @bar() { ret i32 1 } diff --git a/llvm/utils/lit/tests/check-filter.py b/llvm/utils/lit/tests/check-filter.py new file mode 100644 index 0000000000000..5352136c1ea82 --- /dev/null +++ b/llvm/utils/lit/tests/check-filter.py @@ -0,0 +1,23 @@ +# Verify lit's --check filter. --check=N drops every CommandDirective except +# the Nth (0-indexed). + +# --- --check=0: keep only the first RUN --- +# RUN: %{lit} -a --check=0 %{inputs}/check-filter/sample.ll \ +# RUN: | FileCheck --check-prefix=CHECK-0 \ +# RUN: --implicit-check-not="executed command: echo SECOND-RUN" %s +# +# CHECK-0: executed command: echo FIRST-RUN + +# --- --check=1: skip the first, keep the second --- +# RUN: %{lit} -a --check=1 %{inputs}/check-filter/sample.ll \ +# RUN: | FileCheck --check-prefix=CHECK-1 \ +# RUN: --implicit-check-not="executed command: echo FIRST-RUN" %s +# +# CHECK-1: executed command: echo SECOND-RUN + +# --- No filter: both RUNs execute --- +# RUN: %{lit} -a %{inputs}/check-filter/sample.ll \ +# RUN: | FileCheck --check-prefix=NO-FILTER %s +# +# NO-FILTER: executed command: echo FIRST-RUN +# NO-FILTER: executed command: echo SECOND-RUN >From a63ed38900170263b8db07973dba3f3932dde7af Mon Sep 17 00:00:00 2001 From: jofrn <[email protected]> Date: Sat, 30 May 2026 16:52:22 -0700 Subject: [PATCH 2/2] [FileCheck] Add --filter-label to drop CHECKs outside selected CHECK-LABEL sections --- llvm/include/llvm/FileCheck/FileCheck.h | 5 +++ llvm/lib/FileCheck/FileCheck.cpp | 24 ++++++++++++ llvm/lib/FileCheck/FileCheckImpl.h | 5 +++ llvm/test/FileCheck/filter-label.txt | 51 +++++++++++++++++++++++++ llvm/utils/FileCheck/FileCheck.cpp | 12 ++++++ 5 files changed, 97 insertions(+) create mode 100644 llvm/test/FileCheck/filter-label.txt diff --git a/llvm/include/llvm/FileCheck/FileCheck.h b/llvm/include/llvm/FileCheck/FileCheck.h index b000f5b0dac73..fbca647672e9e 100644 --- a/llvm/include/llvm/FileCheck/FileCheck.h +++ b/llvm/include/llvm/FileCheck/FileCheck.h @@ -13,6 +13,7 @@ #ifndef LLVM_FILECHECK_FILECHECK_H #define LLVM_FILECHECK_FILECHECK_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" @@ -502,6 +503,10 @@ class FileCheck { LLVM_ABI StringRef CanonicalizeFile(MemoryBuffer &MB, SmallVectorImpl<char> &OutputBuffer); + /// Drops CHECKs whose CHECK-LABEL section's literal text doesn't match any + /// of \p Names. CHECKs before the first label are dropped. Nop if empty. + LLVM_ABI void filterByLabel(ArrayRef<StringRef> Names); + /// Checks the input to FileCheck provided in the \p Buffer against the /// expected strings read from the check file and record diagnostics emitted /// in \p Diags. Errors are recorded against \p SM. diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp index ae25a078713e7..6a4f9c5a8343b 100644 --- a/llvm/lib/FileCheck/FileCheck.cpp +++ b/llvm/lib/FileCheck/FileCheck.cpp @@ -2531,6 +2531,30 @@ static bool ValidatePrefixes(StringRef Kind, StringSet<> &UniquePrefixes, return true; } +void FileCheck::filterByLabel(ArrayRef<StringRef> Names) { + if (Names.empty()) + return; + + // (name1|name2|...) bracketed by non-chars (or string ends). + std::string Alts; + for (StringRef Name : Names) { + if (!Alts.empty()) + Alts += '|'; + Alts += Regex::escape(Name); + } + Regex Re("(^|[^[:alnum:]_])(" + Alts + ")([^[:alnum:]_]|$)"); + + std::vector<FileCheckString> Filtered; + bool Keeping = false; + for (auto &CS : CheckStrings) { + if (CS.Pat.getCheckTy() == Check::CheckLabel) + Keeping = Re.match(CS.Pat.getFixedStr()); + if (Keeping) + Filtered.push_back(std::move(CS)); + } + CheckStrings = std::move(Filtered); +} + bool FileCheck::ValidateCheckPrefixes() { StringSet<> UniquePrefixes; // Add default prefixes to catch user-supplied duplicates of them below. diff --git a/llvm/lib/FileCheck/FileCheckImpl.h b/llvm/lib/FileCheck/FileCheckImpl.h index 5393650020a30..b156daaf40ab1 100644 --- a/llvm/lib/FileCheck/FileCheckImpl.h +++ b/llvm/lib/FileCheck/FileCheckImpl.h @@ -745,6 +745,11 @@ class Pattern { Check::FileCheckType getCheckTy() const { return CheckTy; } + /// \returns the literal pattern text, or an empty StringRef if the pattern + /// requires a regex match. Used by FileCheck::filterByLabel to identify + /// CHECK-LABEL sections by their literal text. + StringRef getFixedStr() const { return FixedStr; } + int getCount() const { return CheckTy.getCount(); } private: diff --git a/llvm/test/FileCheck/filter-label.txt b/llvm/test/FileCheck/filter-label.txt new file mode 100644 index 0000000000000..db5d5e3df3671 --- /dev/null +++ b/llvm/test/FileCheck/filter-label.txt @@ -0,0 +1,51 @@ +; Verify --filter-label drops CHECKs whose CHECK-LABEL section's literal text +; doesn't match any of the given names. + +; RUN: FileCheck --filter-label=foo -input-file %s %s -check-prefix=ALL +; RUN: FileCheck --filter-label=bar -input-file %s %s -check-prefix=ALL +; RUN: FileCheck --filter-label=foo,bar -input-file %s %s -check-prefix=ALL +; RUN: FileCheck --filter-label=bar,foo -input-file %s %s -check-prefix=ALL + +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck --filter-label=foobar -input-file %s %s -check-prefix=ALL 2>&1 \ +; RUN: | FileCheck %s -check-prefix=FAIL-ERR + +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck -input-file %s %s -check-prefix=ALL 2>&1 \ +; RUN: | FileCheck %s -check-prefix=FAIL-ERR + +; RUN: %ProtectFileCheckOutput \ +; RUN: not FileCheck --filter-label=baz -input-file %s %s -check-prefix=BAZ 2>&1 \ +; RUN: | FileCheck %s -check-prefix=FAIL-ERR + + +@foo: +a +b + +@bar: +c +d + +@foobar: +e +f + +@baz: +g +h + +; ALL-LABEL: @foo: +; ALL: {{^}}a +; ALL: {{^}}b +; ALL-LABEL: @bar: +; ALL: {{^}}c +; ALL: {{^}}d +; ALL-LABEL: @foobar: +; ALL: {{^}}e +; ALL: {{^}}MISMATCH +; BAZ-LABEL: @baz: +; BAZ: {{^}}g +; BAZ: {{^}}i + +; FAIL-ERR: expected string not found in input diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp index 63b745469b8ee..41a1921c014dc 100644 --- a/llvm/utils/FileCheck/FileCheck.cpp +++ b/llvm/utils/FileCheck/FileCheck.cpp @@ -143,6 +143,12 @@ enum DumpInputFilterValue { DumpInputFilterAll }; +static cl::list<std::string> FilterLabel( + "filter-label", cl::CommaSeparated, + cl::desc("Keep only CHECKs in CHECK-LABEL sections whose label\n" + "matches one of the given names."), + cl::value_desc("name")); + static cl::list<DumpInputFilterValue> DumpInputFilters( "dump-input-filter", cl::desc("In the dump requested by -dump-input, print only input lines of\n" @@ -868,6 +874,12 @@ int main(int argc, char **argv) { if (FC.readCheckFile(SM, CheckFileText, &ImpPatBufferIDRange)) return 2; + if (!FilterLabel.empty()) { + SmallVector<StringRef, 4> FilterLabelRefs(FilterLabel.begin(), + FilterLabel.end()); + FC.filterByLabel(FilterLabelRefs); + } + // Open the file to check and add it to SourceMgr. ErrorOr<std::unique_ptr<MemoryBuffer>> InputFileOrErr = MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
