Simon Jakobi pushed to branch wip/sjakobi/T18922-filecheck-llvm-poc at Glasgow Haskell Compiler / GHC
Commits:
-
45242359
by Simon Jakobi at 2026-11-05T06:25:13+01:00
-
ceb5d2cc
by Simon Jakobi at 2026-11-05T07:43:53+01:00
-
81948d02
by Simon Jakobi at 2026-11-05T07:43:53+01:00
8 changed files:
- configure.ac
- hadrian/cfg/system.config.in
- hadrian/src/Oracles/Setting.hs
- hadrian/src/Settings/Builders/RunTest.hs
- testsuite/driver/testglobals.py
- testsuite/driver/testlib.py
- testsuite/filecheck.md
- testsuite/mk/test.mk
Changes:
| ... | ... | @@ -583,6 +583,20 @@ FIND_LLVM_PROG([LLVMAS], [clang], [$LlvmMinVersion], [$LlvmMaxVersion]) |
| 583 | 583 | LlvmAsCmd="$LLVMAS"
|
| 584 | 584 | AC_SUBST([LlvmAsCmd])
|
| 585 | 585 | |
| 586 | +dnl ** Which LLVM FileCheck to use? (testsuite only)
|
|
| 587 | +dnl --------------------------------------------------------------
|
|
| 588 | +# CQ[filecheck-version-range]
|
|
| 589 | +# Q: FileCheck is independent of the LLVM backend, so why the backend's
|
|
| 590 | +# LlvmMaxVersion as the upper bound, and what fixes the floor at 12?
|
|
| 591 | +# A~ FIND_LLVM_PROG needs a finite range to enumerate the suffixed names
|
|
| 592 | +# (FileCheck-NN, FileCheckNN). The testsuite passes
|
|
| 593 | +# --allow-unused-prefixes, which appeared in LLVM 12.
|
|
| 594 | +FileCheckMinVersion=12 # inclusive
|
|
| 595 | +AC_ARG_VAR(FILECHECK,[Use as the path to LLVM's FileCheck, for the testsuite [default=autodetect]])
|
|
| 596 | +FIND_LLVM_PROG([FILECHECK], [FileCheck], [$FileCheckMinVersion], [$LlvmMaxVersion])
|
|
| 597 | +FileCheckCmd="$FILECHECK"
|
|
| 598 | +AC_SUBST([FileCheckCmd])
|
|
| 599 | + |
|
| 586 | 600 | dnl --------------------------------------------------------------
|
| 587 | 601 | dnl End of configure script option section
|
| 588 | 602 | dnl --------------------------------------------------------------
|
| ... | ... | @@ -1077,7 +1091,8 @@ echo "\ |
| 1077 | 1091 | llc : $LlcCmd
|
| 1078 | 1092 | opt : $OptCmd
|
| 1079 | 1093 | llvm-as : $LlvmAsCmd
|
| 1080 | - llvm-dlltool : $DlltoolCmd"
|
|
| 1094 | + llvm-dlltool : $DlltoolCmd
|
|
| 1095 | + FileCheck : $FileCheckCmd"
|
|
| 1081 | 1096 | |
| 1082 | 1097 | if test "$HSCOLOUR" = ""; then
|
| 1083 | 1098 | echo "
|
| ... | ... | @@ -23,6 +23,7 @@ makeinfo = @MAKEINFO@ |
| 23 | 23 | bourne-shell = @SH@
|
| 24 | 24 | git = @GIT@
|
| 25 | 25 | cabal = @CABAL@
|
| 26 | +filecheck = @FileCheckCmd@
|
|
| 26 | 27 | |
| 27 | 28 | # Python 3 is required to run test driver.
|
| 28 | 29 | # See: https://github.com/ghc/ghc/blob/master/testsuite/mk/boilerplate.mk#L220
|
| ... | ... | @@ -62,6 +62,7 @@ data ProjectSetting = |
| 62 | 62 | | HostPlatformFull
|
| 63 | 63 | | BourneShell
|
| 64 | 64 | | EmsdkVersion
|
| 65 | + | FileCheck
|
|
| 65 | 66 | |
| 66 | 67 | -- | Things which configure how a specific stage is built
|
| 67 | 68 | data BuildSetting =
|
| ... | ... | @@ -105,6 +106,7 @@ setting key = lookupSystemConfig $ case key of |
| 105 | 106 | HostPlatformFull -> "host-platform-full"
|
| 106 | 107 | BourneShell -> "bourne-shell"
|
| 107 | 108 | EmsdkVersion -> "emsdk-version"
|
| 109 | + FileCheck -> "filecheck"
|
|
| 108 | 110 | |
| 109 | 111 | buildSetting :: BuildSetting -> Stage -> Action String
|
| 110 | 112 | buildSetting key stage = tgtConfig stage $ case key of
|
| ... | ... | @@ -238,6 +238,7 @@ runTestBuilderArgs = builder Testsuite ? do |
| 238 | 238 | (testEnv, testMetricsFile) <- expr . liftIO $
|
| 239 | 239 | (,) <$> lookupEnv "TEST_ENV" <*> lookupEnv "METRICS_FILE"
|
| 240 | 240 | perfBaseline <- expr . liftIO $ lookupEnv "PERF_BASELINE_COMMIT"
|
| 241 | + filecheck <- expr $ setting FileCheck
|
|
| 241 | 242 | targetWrapper <- expr . liftIO $ lookupEnv "CROSS_EMULATOR"
|
| 242 | 243 | |
| 243 | 244 | threads <- shakeThreads <$> expr getShakeOptions
|
| ... | ... | @@ -282,6 +283,7 @@ runTestBuilderArgs = builder Testsuite ? do |
| 282 | 283 | , arg "-e", arg $ "config.package_conf_cache_file=" ++ show pkgConfCacheFile
|
| 283 | 284 | |
| 284 | 285 | , arg "-e", arg $ "config.libdir=" ++ show libdir
|
| 286 | + , arg "-e", arg $ "config.filecheck=" ++ show filecheck
|
|
| 285 | 287 | |
| 286 | 288 | |
| 287 | 289 | , arg "-e", arg $ "config.have_interp=" ++ show withInterpreter
|
| ... | ... | @@ -129,9 +129,9 @@ class TestConfig: |
| 129 | 129 | # Do we have a functional LLVM toolchain?
|
| 130 | 130 | self.have_llvm = False
|
| 131 | 131 | |
| 132 | - # LLVM's FileCheck, for filecheck_stderr and filecheck_stdout
|
|
| 133 | - # (None: look for it when first needed, see find_filecheck)
|
|
| 134 | - self.filecheck = None # type: Optional[str]
|
|
| 132 | + # LLVM's FileCheck as found by configure, for filecheck_stderr and
|
|
| 133 | + # filecheck_stdout (empty: none found, see find_filecheck)
|
|
| 134 | + self.filecheck = ''
|
|
| 135 | 135 | |
| 136 | 136 | # Do we have vanilla libraries?
|
| 137 | 137 | self.have_vanilla = False
|
| ... | ... | @@ -1295,30 +1295,18 @@ def _filecheck(name, opts, kind, check_file, prefixes): |
| 1295 | 1295 | # have_llvm() tests merely skip; is loud right for a tool only some
|
| 1296 | 1296 | # tests need?
|
| 1297 | 1297 | if find_filecheck() is None:
|
| 1298 | - framework_fail(name, None, 'FileCheck not found: set FILECHECK or '
|
|
| 1299 | - 'config.filecheck, or run testsuite/driver/build_filecheck.py')
|
|
| 1298 | + framework_fail(name, None, 'FileCheck not found: pass FILECHECK=... to '
|
|
| 1299 | + 'configure, or run testsuite/driver/build_filecheck.py')
|
|
| 1300 | 1300 | opts.skip = True
|
| 1301 | 1301 | opts.filecheck[kind] = (name, check_file, prefixes or ['CHECK'])
|
| 1302 | 1302 | |
| 1303 | -# CQ[filecheck-discovery]
|
|
| 1304 | -# Q: should configure find FileCheck like llc (FIND_LLVM_PROG) and hadrian
|
|
| 1305 | -# pass it as config.filecheck, instead of this search from the driver?
|
|
| 1306 | -# CQ[filecheck-version-range]
|
|
| 1307 | -# Q: the versioned names mirror configure's LlvmMinVersion..LlvmMaxVersion;
|
|
| 1308 | -# which FileCheck versions do the tests actually require?
|
|
| 1309 | 1303 | def find_filecheck() -> Optional[str]:
|
| 1310 | - if config.filecheck is None:
|
|
| 1311 | - built = config.top.parent / '_build' / 'filecheck' / 'bin' / 'FileCheck'
|
|
| 1312 | - candidates = [os.environ.get('FILECHECK'), str(built), 'FileCheck'] \
|
|
| 1313 | - + ['FileCheck-%d' % v for v in range(23, 12, -1)]
|
|
| 1314 | - for c in candidates:
|
|
| 1315 | - found = shutil.which(c) if c else None
|
|
| 1316 | - if found:
|
|
| 1317 | - config.filecheck = found
|
|
| 1318 | - break
|
|
| 1319 | - else:
|
|
| 1320 | - config.filecheck = ''
|
|
| 1321 | - return config.filecheck or None
|
|
| 1304 | + built = config.top.parent / '_build' / 'filecheck' / 'bin' / 'FileCheck'
|
|
| 1305 | + for c in [os.environ.get('FILECHECK'), str(built), config.filecheck]:
|
|
| 1306 | + found = shutil.which(c) if c else None
|
|
| 1307 | + if found:
|
|
| 1308 | + return found
|
|
| 1309 | + return None
|
|
| 1322 | 1310 | |
| 1323 | 1311 | def no_check_hp(name, opts):
|
| 1324 | 1312 | opts.check_hp = False
|
| ... | ... | @@ -17,7 +17,7 @@ Installing FileCheck |
| 17 | 17 | Without a `FileCheck` binary every test that uses it is reported as a
|
| 18 | 18 | framework failure and skipped:
|
| 19 | 19 | |
| 20 | - FileCheck not found: set FILECHECK or config.filecheck, or run
|
|
| 20 | + FileCheck not found: pass FILECHECK=... to configure, or run
|
|
| 21 | 21 | testsuite/driver/build_filecheck.py
|
| 22 | 22 | |
| 23 | 23 | Any LLVM 12 or newer should do. The directives and the regex dialect have not
|
| ... | ... | @@ -30,32 +30,49 @@ supported LLVM version range. |
| 30 | 30 | |
| 31 | 31 | In this order, taking the first that exists:
|
| 32 | 32 | |
| 33 | -1. `config.filecheck`, which nothing in configure or hadrian sets yet. It can
|
|
| 34 | - be set with `-e config.filecheck=...` when invoking `runtests.py`
|
|
| 35 | - directly.
|
|
| 36 | -2. The `FILECHECK` environment variable, a path or a command name.
|
|
| 37 | -3. `_build/filecheck/bin/FileCheck` in the GHC tree, where
|
|
| 33 | +1. The `FILECHECK` environment variable at test time, a path or a command
|
|
| 34 | + name.
|
|
| 35 | +2. `_build/filecheck/bin/FileCheck` in the GHC tree, where
|
|
| 38 | 36 | `build_filecheck.py` installs to.
|
| 39 | -4. `FileCheck` on `PATH`, then `FileCheck-23` down to `FileCheck-13`, the
|
|
| 40 | - names Debian-style packages use.
|
|
| 37 | +3. What `./configure` found, passed as `config.filecheck` by hadrian and by
|
|
| 38 | + `make -C testsuite/tests`, which reads it from `hadrian/cfg/system.config`.
|
|
| 39 | + Configure searches `PATH` the way it does for `llc`: `FileCheck-23` down
|
|
| 40 | + to `FileCheck-12` (the Debian and Ubuntu names), `FileCheck23` down to
|
|
| 41 | + `FileCheck12` (the FreeBSD names), then plain `FileCheck`, and rejects a
|
|
| 42 | + version outside that range. `./configure FILECHECK=/path/to/FileCheck`
|
|
| 43 | + overrides the search, which is needed where the binary is not on `PATH`
|
|
| 44 | + (Alpine, Homebrew). The result is in configure's summary under "Using
|
|
| 45 | + LLVM tools".
|
|
| 41 | 46 | |
| 42 | 47 | ### Distribution packages
|
| 43 | 48 | |
| 44 | 49 | Checked against the package file lists or build recipes on 2026-11-05, not by
|
| 45 | -installing each one.
|
|
| 50 | +installing each one. Versions below LLVM 12 do not work (see above).
|
|
| 46 | 51 | |
| 47 | -| System | Package | Binary | Found automatically? |
|
|
| 52 | +| System | Package | Binary | Found by configure? |
|
|
| 48 | 53 | | --- | --- | --- | --- |
|
| 49 | -| Debian, Ubuntu | `llvm-NN-tools` | `/usr/bin/FileCheck-NN` | yes |
|
|
| 54 | +| Debian 11+, Ubuntu 20.04+ | `llvm-NN-tools` | `/usr/bin/FileCheck-NN` | yes |
|
|
| 55 | +| Debian 9, 10, Ubuntu 18.04 | `llvm-NN-tools` from [apt.llvm.org](https://apt.llvm.org) | `/usr/bin/FileCheck-NN` | yes |
|
|
| 50 | 56 | | Fedora | `llvm` | `/usr/bin/FileCheck` | yes |
|
| 57 | +| Rocky 8, 9 | `llvm` | `/usr/bin/FileCheck` | yes |
|
|
| 58 | +| Alpine 3.16+ | `llvmNN-test-utils` | `/usr/lib/llvmNN/bin/FileCheck` | no, pass `FILECHECK=` |
|
|
| 51 | 59 | | Arch | `llvm` | `/usr/bin/FileCheck` | yes |
|
| 52 | -| Homebrew | `llvm` (keg-only) | `$(brew --prefix llvm)/bin/FileCheck` | no, set `FILECHECK` |
|
|
| 60 | +| Homebrew | `llvm` or `llvm@NN` (keg-only) | `$(brew --prefix llvm)/bin/FileCheck` | no, pass `FILECHECK=` |
|
|
| 53 | 61 | | Nix | `llvmPackages.llvm` | `bin/FileCheck` of that package | yes, once in `PATH` |
|
| 54 | -| FreeBSD | `llvmNN` (`LIT` option, on by default) | `/usr/local/bin/FileCheckNN` | no, set `FILECHECK` |
|
|
| 55 | -| MSYS2 | `mingw-w64-<env>-llvm` | `FileCheck.exe` in the environment's `bin` | yes, in that shell |
|
|
| 62 | +| FreeBSD, DragonFly | `llvmNN` (`LIT` option, on by default) | `/usr/local/bin/FileCheckNN` | yes |
|
|
| 63 | +| MSYS2 (all environments, including CLANGARM64) | `mingw-w64-<env>-llvm` | `FileCheck.exe` in the environment's `bin` | yes, in that shell |
|
|
| 56 | 64 | |
| 57 | -Not checked: the official `clang+llvm-*` release bindists (which GHC's CI
|
|
| 58 | -images install LLVM from) and LLVM's Windows installer.
|
|
| 65 | +The Debian and Ubuntu packages exist for amd64, i386, arm64, armhf, ppc64el,
|
|
| 66 | +s390x, riscv64 and loong64 (loong64 in unstable only).
|
|
| 67 | + |
|
| 68 | +Sources that do **not** ship FileCheck, so use the build script below:
|
|
| 69 | + |
|
| 70 | +- the official `clang+llvm-*` release tarballs and the LLVM Windows
|
|
| 71 | + installer, which are built without `LLVM_INSTALL_UTILS`
|
|
| 72 | +- llvm-mingw
|
|
| 73 | +- MacPorts `llvm-NN`
|
|
| 74 | +- OpenBSD's `devel/llvm`, which removes it on purpose
|
|
| 75 | +- Alpine 3.12 to 3.15, whose only `-test-utils` package is LLVM 10
|
|
| 59 | 76 | |
| 60 | 77 | The Debian package is the cheapest route: it is a few hundred kB and does not
|
| 61 | 78 | pull in the rest of LLVM. For example, on Ubuntu 24.04:
|
| ... | ... | @@ -284,6 +284,15 @@ RUNTEST_OPTS += --config 'stats_files_dir=$(TOP)/tests/perf/haddock' |
| 284 | 284 | |
| 285 | 285 | RUNTEST_OPTS += -e "config.stage=$(GhcStage)"
|
| 286 | 286 | |
| 287 | +# CQ[filecheck-make-path]
|
|
| 288 | +# Q: Why read hadrian's system.config here, when nothing else in the make
|
|
| 289 | +# path depends on ./configure having run?
|
|
| 290 | +# A~ FileCheck is a testsuite tool, not a compiler setting, so ghc --info
|
|
| 291 | +# cannot supply it. An unconfigured tree yields an empty value and the
|
|
| 292 | +# driver falls back to $FILECHECK or _build/filecheck (see find_filecheck).
|
|
| 293 | +FILECHECK ?= $(shell sed -n 's/^filecheck *= *//p' '$(TOP)/../hadrian/cfg/system.config' 2>/dev/null)
|
|
| 294 | +RUNTEST_OPTS += -e 'config.filecheck=r"$(FILECHECK)"'
|
|
| 295 | + |
|
| 287 | 296 | ifneq "$(METRICS_FILE)" ""
|
| 288 | 297 | RUNTEST_OPTS += \
|
| 289 | 298 | --metrics-file "$(METRICS_FILE)"
|