Fuzzing to fill LLVM coverage gaps with fuzz-generated tests.
fuzz-fill identifies coverage gaps in LLVM:
- Gap finding — measure baseline coverage achieved by LLVM's LIT test suite and produce a list of uncovered lines. At present
fuzz-fillsupports finding gaps in the AMDGPU and SPIR-V backends; this will be extended in the future to other backends and parts of the LLVM codebase.- Baseline — all uncovered lines in a user-specified part of the LLVM codebase.
- PR — added or changed lines in a commit that baseline coverage still misses.
- Gap filling — run a fuzz corpus against the gap list and report which gaps each candidate test covers.
- Reduction — scaffold and run testcase reduction for the first N gap-fill hits, producing minimal LIT-ready tests.
┌─ Gap finding ────────────────────────────────────────────────┐
│ Baseline ──┐ │
│ ├──► Uncovered lines (CSV) │
│ PR ────────┘ │
└───────────────────────────────┬──────────────────────────────┘
▼
┌─ Gap filling ──────────────────────────────────────────────────┐
│ candidate-test ──► incremental ──► new_coverage.csv │
└───────────────────────────────┬──────────────────────────────┘
▼
┌─ Reduction ──────────────────────────────────────────────────┐
│ batch-from-coverage (-n N) ──► t-00001-*/ … reduced.* │
└──────────────────────────────────────────────────────────────┘
See Contributions for tests contributed to LLVM.
Try PR gap finding, which reports lines changed in a PR or commit that are not covered by any LIT tests. Results are saved in <output-dir>/commit_lines_report/target_lines_uncovered.csv. See Gap finding (PR) and Docker test image for more options.
Prerequisites:
- Docker
- A local
llvm-projectcheckout
git clone https://github.com/ROCm/fuzz-fill.git
cd fuzz-fillLLVM pull request — requires GitHub CLI (gh). Builds a PR image, squashes all commits onto the PR's base commit, and finds all coverage gaps in that PR:
./scripts/docker/gap-finding-pr.sh \
--build-image \
--llvm-repo /path/to/llvm-project \
--pr-id 203468 \
--backend-tests amdgpu \
--output-dir ./data/gap-finding-pr-203468 \
-j "$(nproc)"Use spirv instead of amdgpu for SPIR-V backend tests.
Local commit — build from your llvm-project checkout (the local checkout remains unchanged). This command only finds gaps in lines changed in a single commit rather than a full PR. Replace HEAD with a hash, branch, or main~3 as needed:
./scripts/docker/build-image.sh --llvm-dir /path/to/llvm-project --allowlist amdgpu -j "$(nproc)"
./scripts/docker/gap-finding-pr.sh \
--image fuzz-fill-test:latest \
--output-dir ./data/my-commit \
--commit HEAD \
-j "$(nproc)"- Quick start (Docker)
- Setup
- Gap finding (baseline)
- Gap finding (PR)
- Gap filling
- Reduction
- CLI reference
- Docker test image
- Tests
- Contributions
python3 -m venv venv
source venv/bin/activate
pip install -e .You need an official LLVM GitHub release as bootstrap and one SanitizerCoverage build of llvm-project at the matching tag:
| Component | Purpose | How |
|---|---|---|
| Release bootstrap | clang, clang++ for compiling LLVM |
Download LLVM release (e.g. LLVM-22.1.8-Linux-X64.tar.xz) |
| SanitizerCoverage | Instrumented LLVM tree (llc, opt, sancov, …) |
./scripts/build-llvm-sancov.sh ./scripts/allowlist-amdgpu.txt llvm-project llvm-project/build-sancov --bootstrap-bin /path/to/LLVM-22.1.8/bin |
build-llvm-sancov.sh builds one instrumented RelWithDebInfo tree (ninja all). Bootstrap supplies clang/clang++ only.
AMDGPU builds use scripts/allowlist-amdgpu.txt (src:*/lib/Target/AMDGPU/*); SPIRV uses scripts/allowlist-spirv.txt.
python -m coverage baseline patches <instrumented-build>/test/lit.site.cfg.py so LIT forwards UBSAN_OPTIONS to every test subprocess. The patch is idempotent and is re-applied if CMake regenerates that file.
The example scripts below assume paths like:
$LLVM/build-sancov/bin— instrumented build (llvm-lit,llc,opt,sancov, …)
Adjust these to match your trees before running.
When to use this: you want a list of source lines that baseline LIT coverage does not fully hit in a target area (e.g. AMDGPU CodeGen).
Reference script: scripts/gap-finding-baseline.sh
baseline → line_coverage_uncovered.csv
(LIT run) (+ llc_address_line_map.csv)
coverage baseline — identify uncovered lines by running a filtered slice of LLVM instrumented with SanitizerCoverage.
Required flags mirror the Docker gap-finding runners: --output-dir, LIT filters via --lit-filter or --backend-tests, plus explicit LLVM paths for local builds.
| Flag | Meaning |
|---|---|
--output-dir |
Root for artifacts |
--llvm-repo |
Path to your llvm-project checkout |
--llvm-bin |
Uninstrumented bin directory (sancov) |
--instrumented-bin-dir |
SanitizerCoverage bin directory (llvm-lit, llc, opt) |
--lit-filter |
LIT --filter= regex; repeat for multiple |
--backend-tests |
amdgpu or spirv — default LIT filter when --lit-filter is omitted |
-j, --jobs |
Parallel jobs for llvm-lit |
./scripts/gap-finding-baseline.sh \
--output-dir ./data/baseline-run \
--llvm-repo /path/llvm-project \
--llvm-bin /path/llvm-project/build/bin \
--instrumented-bin-dir /path/llvm-project/build-sancov/bin \
--backend-tests amdgpu \
-j "$(nproc)"Under $OUTPUT_DIR/baseline/:
| Path | Contents |
|---|---|
line_coverage_summary.csv |
Per-line baseline coverage: covered, partially, or uncovered |
line_coverage_uncovered.csv |
Main gap list — input to target-lines and downstream gap filling |
<tool>_address_line_map.csv |
Per-tool address-to-line map (e.g. llc_address_line_map.csv) |
<tool>_line_point_summary.csv |
Per-tool covered/all points per (file, line) |
lit_failures.json |
Failed LIT tests (name, code, output, elapsed) |
COMMIT |
Revision of the LLVM source tree the baseline ran against (omitted for a non-git tree) |
processed_sancov/ |
Per-tool merged .sancov / .symcov |
When to use this: you landed a patch and want added source lines that baseline coverage still does not fully cover.
Reference script: scripts/gap-finding-pr.sh
added-lines → baseline → target-lines
(from git) (LIT run) (uncovered added lines)
added-lines— parsegit showfor a commit and list every line added on the right-hand side of the diff.baseline— same baseline run as gap finding (baseline).target-lines— include each added line whose(file, line)appears inline_coverage_uncovered.csv.
Step 3 does not re-run LIT; you can repeat it with different added-lines.csv inputs while baseline artifacts remain valid.
| Flag | Meaning |
|---|---|
--output-dir |
Root for all artifacts |
--commit |
Revision to analyse (HEAD, a hash, main~3, …) |
--pr-id |
GitHub PR number — squashes PR into .fuzz-fill-llvm-pr-worktrees/pr-<n>/ (exclusive with --commit) |
--github-repo |
With --pr-id (default: llvm/llvm-project) |
--llvm-repo |
llvm-project checkout (added-lines tree; with --pr-id, used as clone reference) |
--llvm-bin / --instrumented-bin-dir |
Same as baseline gap finding |
--lit-filter / --backend-tests |
LIT filters for the baseline step |
-j, --jobs |
Parallel jobs for llvm-lit |
PR gap finding squashes all PR commits onto the merge-base in a self-contained worktree (same as the Docker PR image build). Use scripts/prepare-pr-llvm.sh directly if you only need the squashed tree:
./scripts/prepare-pr-llvm.sh \
--pr-id 214457 \
--dest ./.fuzz-fill-llvm-pr-worktrees/pr-214457/llvm-project \
--reference /path/llvm-project \
--squash-commit-file ./.fuzz-fill-llvm-pr-worktrees/pr-214457/squash-commit./scripts/gap-finding-pr.sh \
--output-dir ./data/gap-finding-pr \
--llvm-repo /path/llvm-project \
--llvm-bin /path/llvm-project/build/bin \
--instrumented-bin-dir /path/llvm-project/build-sancov/bin \
--commit HEAD \
--backend-tests amdgpu \
-j "$(nproc)"GitHub pull request (squash + gap finding):
./scripts/gap-finding-pr.sh \
--output-dir ./data/gap-finding-pr-214457 \
--llvm-repo /path/llvm-project \
--llvm-bin ./.fuzz-fill-llvm-pr-worktrees/pr-214457/llvm-project/build-sancov/bin \
--instrumented-bin-dir ./.fuzz-fill-llvm-pr-worktrees/pr-214457/llvm-project/build-sancov/bin \
--pr-id 214457 \
--backend-tests amdgpu \
-j "$(nproc)"Under $OUTPUT_DIR:
| Path | Contents |
|---|---|
added-lines/added-lines.csv |
Added lines from the commit (path, line_no, text) |
added-lines/COMMIT |
Resolved hash of --commit |
baseline/line_coverage_uncovered.csv |
Baseline uncovered lines |
baseline/llc_address_line_map.csv |
LLC map from the baseline run |
baseline/lit_failures.json |
Failed LIT tests from baseline |
commit_lines_report/target_lines_uncovered.csv |
Main result — PR-added lines still uncovered (file, line, optional text; absolute paths) |
scripts/gap-filling.sh runs coverage candidate-test then coverage incremental against a gap list from gap finding or PR gap finding.
Inputs: line_coverage_uncovered.csv (or target_lines_uncovered.csv) plus matching llc_address_line_map.csv from the same baseline run, and a fuzz corpus directory.
Main output: <output-dir>/incremental/new_coverage.csv with columns test_name, file, line, covered-points.
./scripts/gap-filling.sh \
--output-dir ./data/gap-fill-out \
--line-coverage-uncovered-csv ./data/baseline/line_coverage_uncovered.csv \
--llc-address-line-map-csv ./data/baseline/llc_address_line_map.csv \
--candidate-tests-dir ./candidate-tests-dataset/amdgcn-amd-amdhsa \
-n 100 \
--llvm-repo /path/llvm-project \
--llvm-bin /path/llvm-project/build/bin \
--instrumented-bin-dir /path/llvm-project/build-sancov/bin \
-j "$(nproc)"Docker: scripts/docker/gap-filling.sh — same flags plus --image / --pr-id.
scripts/reduction.sh is a thin wrapper around python -m reduce batch-from-coverage. It scaffolds (and optionally runs) reduction for the first N rows of incremental/new_coverage.csv from a gap-fill output directory. Each row becomes a case directory t-00001-<short>/ with config.json, interesting_ir.sh, and a copied .bc. The IR interestingness script is rendered from src/reduce/template_interesting_ir.sh.
Default output: <gap-fill-dir>/reduced/ (override with --output).
# Scaffold one hit (inspect harness before running llvm-reduce):
./scripts/reduction.sh \
--gap-fill-dir ./data/gap-fill-out \
-n 1 \
--scaffold-only
# Scaffold and reduce the first three hits:
./scripts/reduction.sh \
--gap-fill-dir ./data/gap-fill-out \
-n 3 \
--llvm-bin /path/llvm-project/build/bin \
--instrumented-bin-dir /path/llvm-project/build-sancov/bin \
--pipeline llvm_reduce_irDocker: scripts/docker/reduction.sh.
Single-case reduction (hand-written harness under example/):
python -m reduce \
--config example/amd/si-i1-copies/config.json \
--llc /path/llvm-project/build-sancov/bin/llc \
--llvm-reduce /path/llvm-project/build/bin/llvm-reduceCLI (python -m reduce batch-from-coverage --help):
python -m reduce batch-from-coverage \
--gap-fill-dir ./data/gap-fill-out \
-n 3 \
--scaffold-onlySee --help for pipeline options (--pipeline, --pass-under-test, --mtriple, --with-creduce, …). LLVM tools can be passed explicitly (--llc, --llvm-reduce), via --llvm-bin / --instrumented-bin-dir, or through FUZZ_FILL_LLC / FUZZ_FILL_LLVM_REDUCE environment variables.
Integration test: integration-tests/coverage-pipeline.test (real gap-fill pipeline plus llvm-reduce on the first hit).
Reduction runs an ordered pipeline of passes (see config.json or --pipeline). Each pass reads the current test artifact and writes the next intermediate under reduced/tmp/; the final artifact is copied to reduced/reduced.ll (or reduced.mir for MIR pipelines).
| Pass id | Description |
|---|---|
snapshot |
Copy the input IR into the temp directory as a baseline checkpoint before later passes mutate it. |
llvm_reduce_ir |
Run llvm-reduce on LLVM IR (.ll or .bc) using an interesting-ness script (interesting_ir.sh) that checks SanitizerCoverage still hits the target line. For .bc input, disassembles the reduced bitcode to .ll with llvm-dis. Default batch pipeline. |
creduce |
Further shrink the current artifact with C-Reduce, using a copy of the interesting-ness script where "$1" is replaced by the candidate filename (C-Reduce runs tests in its own temp directory). Optional follow-on to llvm_reduce_ir or llvm_reduce_mir (--with-creduce). |
extract_mir_before_pass |
Run llc with -stop-before=<pass_under_test> -simplify-mir to extract MIR immediately before a specific machine pass. Requires pass_under_test, mtriple, and llc_O on the step. Typical first step before llvm_reduce_mir. |
llvm_reduce_mir |
Run llvm-reduce -x=mir on MIR using interesting_mir.sh, which re-runs llc through the pass under test and checks coverage. Requires --pass-under-test when scaffolding batch cases. |
extract_ir_before_pass |
Like extract_mir_before_pass, but stops before the pass without -simplify-mir and normalizes llc's print-before dump (strip --- delimiters and common indentation) into valid LLVM IR for downstream IR reduction. |
On the default AMDGPU (SelectionDAG) codegen path, the module stays in LLVM IR until amdgpu-isel (AMDGPUISelDAGToDAG). That pass is the instruction selector; everything before it is IR-only. To capture that final IR snapshot, use extract_ir_before_pass with pass_under_test set to amdgpu-isel:
{
"id": "extract_ir_before_pass",
"parameters": {
"pass_under_test": "amdgpu-isel",
"mtriple": "amdgcn-amd-amdhsa",
"llc_O": "-O3"
}
}The same stop point behaves differently depending on -simplify-mir:
llc invocation |
Output |
|---|---|
-stop-before=amdgpu-isel (no -simplify-mir) |
Pure LLVM IR — what extract_ir_before_pass writes as .ll |
-stop-before=amdgpu-isel -simplify-mir |
Hybrid snapshot: IR plus empty MachineFunction shells — what extract_mir_before_pass produces; not suitable for llvm-reduce -x=mir until real machine instructions exist |
Use --mir-codegen-only when the pass under test is codegen-only (like amdgpu-isel): interesting_mir_codegen.sh resumes from the extracted snapshot with normal llc flags instead of -run-pass.
Other backends: the IR→MIR boundary depends on the selector in use. GlobalISel (e.g. -global-isel=1 on AMDGPU) crosses at irtranslator, not amdgpu-isel. SPIR-V IR-only extraction often stops before target IR passes such as spirv-emit-intrinsics; the GlobalISel MIR boundary is instruction-select. Mid-pipeline target IR passes (e.g. amdgpu-remove-incompatible-functions) are a separate use case — they extract IR before a specific pass, not necessarily at the last IR-only point.
Pass ids are comma-separated for --pipeline (e.g. extract_mir_before_pass,llvm_reduce_mir,creduce). Extraction passes and llvm_reduce_mir need --pass-under-test and --mtriple; see --help on batch-from-coverage for --extract-mir-output and --extract-ir-output.
The scripts above call these modules. Use --help on any command for the full flag list.
| Command | Role |
|---|---|
python -m coverage baseline |
Baseline LIT coverage (gap finding) |
python -m coverage candidate-test |
Coverage from a fuzz-generated test corpus (gap filling) |
python -m coverage incremental |
Suite gaps filled by fuzz tests (gap filling) |
python -m coverage target-lines |
PR added lines vs line_coverage_uncovered.csv |
python -m added_lines |
Lines added by a git commit |
python -m reduce |
Single-case testcase reduction (--config) |
python -m reduce batch-from-coverage |
Scaffold (and optionally run) reduction for the first N gap-fill hits |
PR gap finding output and baseline gap lists both use an uncovered-lines CSV with columns file and line. Paths are absolute and must match those in llc_address_line_map.csv (as produced by coverage baseline).
| File | Role |
|---|---|
line_coverage_uncovered.csv |
Baseline gap list |
target_lines_uncovered.csv |
PR-added lines still uncovered; same file/line schema, optional text |
PR gap finding input to target-lines remains added-lines.csv (path, line_no, text with git-relative paths). The output report uses the shared contract above.
| Flag | Meaning |
|---|---|
--lit-filter DIR |
LIT --filter= regex; repeat for multiple (OR'd) |
Default when omitted: (?:^|/)AMDGPU(?:/|$). --backend-tests amdgpu|spirv selects the matching AMDGPU or SPIRV pattern.
Baseline symcov CSVs include all instrumented source paths from the LIT run. Use --source-filter on coverage incremental to scope gap finding (default: (?:^|/)llvm/lib/; see DEFAULT_SOURCE_CODE_FILTER in src/coverage/constants.py).
Both artifacts fed to target-lines must come from the same LLVM tree; a stale baseline otherwise reports coincidental line-number matches as gaps.
| Flag | Meaning |
|---|---|
--commit-check / --no-commit-check |
Compare the revisions recorded in the COMMIT files written by coverage baseline and added_lines against HEAD of --llvm-repo (default: enabled) |
--source-text-check / --no-source-text-check |
Compare each target line text against the source on disk at that line number (default: enabled) |
coverage baseline writes COMMIT with the revision of the source tree configured in the build's lit.site.cfg.py; added_lines writes COMMIT with the resolved hash of --commit. Either file being absent (for example a baseline from a non-git source tree) skips the revision check. The source-text check is the one that catches uncommitted local changes, which share a revision. Both stop with an error when unsatisfied.
| Flag | Meaning |
|---|---|
--source-filter REGEX |
Only consider baseline gaps whose source file path matches this Python regex (default: `(?:^ |
Each row in --settings-csv is one llc invocation (flags passed before the input path). The CSV must have a single column header llc_flags:
llc_flags
-O0
-O1
-O2
-O3When --settings-csv is omitted, fuzz-fill uses the built-in default: -O0, -O1, -O2, -O3 (4 runs per input file). Total jobs = (number of .ll/.bc files) × (rows in settings).
By default, every .ll/.bc file under --candidate-tests-dir is processed (sorted path order). Pass --n N to run only the first N inputs.
Several commands accept LLVM tool paths via environment variables when the matching CLI flag is omitted. A flag on the command line always wins.
| Env var | CLI flag | Commands |
|---|---|---|
FUZZ_FILL_SANCOV |
--sancov |
coverage baseline, coverage incremental, coverage min-candidate-tests (mainly for maintainers) |
FUZZ_FILL_LLVM_LIT |
--llvm-lit |
coverage baseline |
FUZZ_FILL_LLC |
--llc |
coverage baseline, coverage candidate-test, reduce |
FUZZ_FILL_OPT |
--opt |
coverage baseline |
FUZZ_FILL_LLVM_REDUCE |
--llvm-reduce |
reduce |
FUZZ_FILL_LLVM_DIS |
--llvm-dis |
reduce (required for .bc input with llvm_reduce_ir) |
FUZZ_FILL_LLVM_REPO |
--llvm-repo |
added_lines, coverage target-lines |
The Docker test image sets these per-tool defaults so interactive container use can omit tool flags. Integration tests use explicit CLI flags instead (see Integration tests).
Example (paths match the Docker test image):
export FUZZ_FILL_SANCOV=/work/llvm-build-sancov/bin/sancov
export FUZZ_FILL_LLVM_LIT=/work/llvm-build-sancov/bin/llvm-lit
export FUZZ_FILL_LLC=/work/llvm-build-sancov/bin/llc
export FUZZ_FILL_OPT=/work/llvm-build-sancov/bin/opt
export FUZZ_FILL_LLVM_REPO=/work/llvm-project
python -m coverage baseline --output-dir data/baseline
python -m added_lines --commit HEADCodeGen-only baseline:
python -m coverage baseline \
--output-dir data/baseline-codegen \
--lit-filter CodeGen/AMDGPUOr via the reference script:
./scripts/gap-finding-baseline.sh \
--output-dir ./data/baseline-codegen \
--llvm-repo /path/llvm-project \
--llvm-bin /path/llvm-project/build/bin \
--instrumented-bin-dir /path/llvm-project/build-sancov/bin \
--lit-filter CodeGen/AMDGPUWorkflow shell scripts under scripts/ may use their own names (LLVM_BIN, INSTRUMENTED_BIN_DIR, …); only the FUZZ_FILL_* variables are read by the Python CLIs.
Optional commands outside the main workflows in fuzz-fill.
| Command | Purpose |
|---|---|
python -m coverage min-candidate-tests |
From candidate-test output, selects a minimal set of tests that cover the same SanitizerCoverage instrumentation points; writes min_candidate_tests.csv, min_candidate_tests_points.csv, and min_candidate_tests_source_files.csv. Requires candidate_test_manifest.csv and candidate_test_settings.csv emitted by candidate-test. |
The Docker image bundles an official LLVM release bootstrap, an instrumented SanitizerCoverage LLVM tree, and a fuzz-fill venv. Use it when you want to run integration tests or experiment without building LLVM locally.
Scripts (under scripts/docker/): build-image.sh, build-image-pr.sh, ensure-image.sh, gap-finding-baseline.sh, gap-finding-pr.sh, gap-filling.sh, reduction.sh, run-full-workflow.sh, test-image.sh, tmp-container.sh
The image bakes a copy of fuzz-fill at /work/fuzz-fill when built. Pass --bind-repo on a docker runner to mount your local checkout over that path (venv stays at /work/fuzz-fill-venv) when you need code that is newer than the image.
From the repo root (first build compiles LLVM and can take a while):
./scripts/docker/build-image.shBy default the image is tagged fuzz-fill-test:latest. LLVM source is downloaded at tag llvmorg-22.1.8 and bootstrapped from the matching GitHub release.
| Option | Meaning |
|---|---|
--llvm-dir <path> |
Use a local llvm-project checkout instead of downloading tagged source |
--llvm-release-version <ver> |
Official LLVM release for bootstrap toolchain (default: 22.1.8) |
--tag <tag> |
Docker image tag (default: latest) |
--allowlist amdgpu|spirv |
SanitizerCoverage allowlist baked into the instrumented build (default: amdgpu) |
--sancov-instrumentation-mode func|bb|edge |
SanitizerCoverage instrumentation mode (default: bb; produces -fsanitize-coverage=<mode>,trace-pc-guard) |
-j <n>, --jobs <n> |
Limit ninja parallelism for the sancov build (default: unconstrained) |
Examples:
./scripts/docker/build-image.sh --llvm-dir llvm-project --tag local-llvm
./scripts/docker/build-image.sh --allowlist spirv --tag spirv
./scripts/docker/build-image.sh --sancov-instrumentation-mode edge --tag edge
./scripts/docker/build-image.sh -j "$(nproc)"Use this image with --image fuzz-fill-test:latest for local-commit gap finding (see Quick start).
scripts/docker/build-image-pr.sh builds a Docker image from an LLVM PR. Pass a local llvm-project clone; the PR is squashed in a standalone fuzz-fill clone so your llvm checkout is unchanged. Requires local gh and Docker (BuildKit). PRs are assumed to live on llvm/llvm-project unless you pass --github-repo.
./scripts/docker/build-image-pr.sh --llvm-repo /path/llvm-project --pr-id 185430 --allowlist amdgpu| Option | Meaning |
|---|---|
--llvm-repo <path> |
Local llvm-project clone |
--pr-id <n> |
GitHub pull request number |
--allowlist amdgpu|spirv |
SanitizerCoverage allowlist |
--sancov-instrumentation-mode func|bb|edge |
SanitizerCoverage instrumentation mode (default: bb) |
--github-repo <owner/repo> |
GitHub repo hosting the PR (default: llvm/llvm-project) |
-j <n>, --jobs <n> |
Limit ninja parallelism for both LLVM builds (default: unconstrained) |
For build + gap finding in one step, use gap-finding-pr.sh --build-image instead.
Gap-finding Docker runners share ensure-image.sh for PR images tagged fuzz-fill-test:llvm-pr-<n>:
| Flag | Meaning |
|---|---|
--build-image |
Build via build-image-pr.sh when the tag is missing |
--force-build |
Rebuild even when the tag already exists |
--keep-image |
Do not remove the image after a --build-image run (default: remove) |
--pr-id <n> |
Select fuzz-fill-test:llvm-pr-<n> |
--llvm-repo <path> |
Required with --build-image |
--backend-tests amdgpu|spirv |
Required with --build-image |
Build once, then reuse on later runs (omit --build-image):
./scripts/docker/gap-finding-pr.sh \
--build-image --keep-image \
--llvm-repo /path/llvm-project --pr-id 203468 \
--backend-tests amdgpu --output-dir ./data/gap-finding-pr-203468 -j "$(nproc)"
./scripts/docker/gap-finding-pr.sh \
--pr-id 203468 --output-dir ./data/gap-finding-pr-203468scripts/docker/gap-finding-baseline.sh runs coverage baseline in a container. Output: <output-dir>/baseline/.
./scripts/docker/gap-finding-baseline.sh \
--output-dir ./data/baseline-run \
-j "$(nproc)"| Option | Meaning |
|---|---|
--output-dir <path> |
Host output directory (required) |
--image <ref> |
Docker image (default: fuzz-fill-test:latest) |
--pr-id <n> |
PR image fuzz-fill-test:llvm-pr-<n> |
--build-image |
Build PR image when missing (see PR image build and reuse) |
--bind-repo |
Mount local fuzz-fill checkout over /work/fuzz-fill |
--lit-filter <prefix> |
LIT filter override (default: from image /work/.sancov-allowlist) |
-j <n>, --jobs <n> |
Parallel jobs for llvm-lit and ninja (when building) |
scripts/docker/gap-finding-pr.sh runs baseline → added_lines → target-lines. Requires --image or --pr-id.
GitHub PR (build + run):
./scripts/docker/gap-finding-pr.sh \
--build-image \
--llvm-repo /path/llvm-project \
--pr-id 203468 \
--backend-tests amdgpu \
--output-dir ./data/gap-finding-pr-203468 \
-j "$(nproc)"Local commit (after build-image.sh):
./scripts/docker/gap-finding-pr.sh \
--image fuzz-fill-test:latest \
--output-dir ./data/my-commit \
--commit HEAD \
-j "$(nproc)"For AMDGPU images, baseline defaults to (?:^|/)AMDGPU(?:/|$); SPIRV to (?:^|/)SPIRV(?:/|$). Override with --lit-filter.
| Option | Meaning |
|---|---|
--image <ref> |
Docker image (required unless --pr-id) |
--pr-id <n> |
PR image tag llvm-pr-<n> (required unless --image) |
--commit <rev> |
Revision for added_lines (default: HEAD in container llvm-project) |
--build-image |
Build PR image when missing |
--force-build / --keep-image |
Rebuild or retain PR image |
--llvm-repo <path> |
Required with --build-image |
--backend-tests amdgpu|spirv |
Required with --build-image |
--output-dir <path> |
Host output directory |
-j <n>, --jobs <n> |
Parallel jobs |
--lit-filter <dir> |
LIT prefix; repeat for multiple |
--github-repo <owner/repo> |
When building (default: llvm/llvm-project) |
Main output: <output-dir>/commit_lines_report/target_lines_uncovered.csv.
scripts/docker/gap-filling.sh runs candidate-test and incremental in a container. Requires gap-list CSV paths and a bind-mounted corpus.
./scripts/docker/gap-filling.sh \
--output-dir ./data/gap-fill-out \
--line-coverage-uncovered-csv ./data/baseline/line_coverage_uncovered.csv \
--llc-address-line-map-csv ./data/baseline/llc_address_line_map.csv \
--candidate-tests-dir /path/to/corpus \
-n 100 \
-j "$(nproc)"Main output: <output-dir>/incremental/new_coverage.csv.
scripts/docker/reduction.sh runs batch-from-coverage in a container. Gap-fill output is mounted read-only at /mounted-gap-fill/; case directories are written under --output (default: <gap-fill-dir>/reduced/).
./scripts/docker/reduction.sh \
--bind-repo \
--gap-fill-dir ./data/gap-fill-out \
--corpus-dir ./candidate-tests-dataset/amdgcn-amd-amdhsa \
-n 1
./scripts/docker/reduction.sh \
--gap-fill-dir ./data/gap-fill-out \
-n 3 \
--scaffold-onlyPass --corpus-dir when gap filling used a bind-mounted fuzz corpus (required so reduction can resolve each test’s input .bc/.ll from test.sh). For .bc inputs, full reduction also needs llvm-dis (the image sets FUZZ_FILL_LLVM_DIS; with --bind-repo, your local checkout supplies the wiring).
scripts/docker/run-full-workflow.sh runs the full pipeline in one shot: gap finding (baseline) → gap filling → reduction. It is intended for local smoke testing of the Docker runners.
Prerequisites: a built Docker image (build-image.sh) and, for a real fuzz corpus, a local checkout of the candidate tests (for example ./candidate-tests-dataset/amdgcn-amd-amdhsa — not baked into the image; gap filling bind-mounts it from the host).
--bind-repo is on by default — every step mounts your local fuzz-fill checkout at /work/fuzz-fill (venv stays at /work/fuzz-fill-venv). Use --no-bind-repo to run the copy baked into the image instead.
Quick smoke test (tiny fixture corpus, scaffold-only reduction):
./scripts/docker/run-full-workflow.shFull reduction with the AMDGPU fuzz corpus:
CORPUS=./candidate-tests-dataset/amdgcn-amd-amdhsa \
GAP_FILL_N=20 \
FULL_REDUCE=1 \
./scripts/docker/run-full-workflow.shBuild the image first, then run the workflow:
LLVM=/path/to/llvm-project ./scripts/docker/run-full-workflow.sh --build-image| Option / env | Meaning |
|---|---|
--build-image |
Build fuzz-fill-test:latest before running (pass LLVM=... if not ../llvm-project) |
--no-bind-repo |
Use fuzz-fill from the image instead of mounting the local checkout |
DATA |
Output root (default: ./data/workflow-test) |
CORPUS |
Fuzz corpus for gap filling and reduction (default: integration-tests/fixtures/coverage-new-tests) |
GAP_FILL_N |
Run the first N candidate tests from CORPUS (default: 1) |
REDUCE_N |
Reduce the first N rows of new_coverage.csv (default: 1) |
FULL_REDUCE |
If 1, run llvm-reduce (default: 0 = scaffold-only) |
J |
Parallel jobs (default: nproc) |
On success, outputs land under $DATA (default ./data/workflow-test/):
| Path | Contents |
|---|---|
gap-finding/baseline/ |
Baseline gap list |
gap-fill/incremental/new_coverage.csv |
Gap-fill hits |
gap-fill/reduced/t-00001-*/ |
Reduction harness (and reduced/reduced.ll when FULL_REDUCE=1) |
By default, reduction processes the first row of new_coverage.csv. To target a specific test, reorder or filter that CSV before re-running reduction.sh, or increase REDUCE_N.
./scripts/docker/test-image.shThis runs the full suite under integration-tests/ using the image baked into the container. Pass the same --tag you used when building:
./scripts/docker/build-image.sh --tag local-llvm
./scripts/docker/test-image.sh --tag local-llvmAfter building a PR image, pass the same tag (default llvm-pr-<pr-id>):
./scripts/docker/build-image-pr.sh --llvm-repo ../llvm-project --pr-id 185430 --allowlist amdgpu
./scripts/docker/test-image.sh --tag llvm-pr-185430Use --bind-repo to mount your local fuzz-fill checkout over /work/fuzz-fill while keeping the image venv at /work/fuzz-fill-venv:
./scripts/docker/test-image.sh --bind-repoAny extra arguments are forwarded to lit. For example:
./scripts/docker/test-image.sh --tag local-llvm integration-tests/smoke.testFor an interactive shell or arbitrary commands:
./scripts/docker/tmp-container.sh # interactive shell
./scripts/docker/tmp-container.sh --bind-repo # shell with host repo mounted
./scripts/docker/tmp-container.sh --bind-repo <command> [args] # one-shot commandWithout --bind-repo, the container uses the fuzz-fill copy baked into the image.
With --bind-repo, your local checkout is mounted at /work/fuzz-fill; the venv stays at /work/fuzz-fill-venv.
To run integration tests manually inside the container:
./scripts/docker/tmp-container.sh ./integration-tests/test.sh \
--venv /work/fuzz-fill-venv \
--llvm-build /work/llvm-build-sancov/bin \
--llvm-sancov-build /work/llvm-build-sancov/bin \
--llvm-src /work/llvm-project \
-v integration-tests/Python unit tests live under tests/. They use the stdlib unittest runner and do not need LLVM builds.
source venv/bin/activate
python -m unittest discover -s tests -vTo run a single module:
python -m unittest tests.test_analyser -vIntegration tests need the SanitizerCoverage LLVM bin directory and llvm-project source. Locally:
./integration-tests/test.sh \
--venv ./venv/ \
--llvm-build llvm-project/build-sancov/bin/ \
--llvm-sancov-build llvm-project/build-sancov/bin/ \
--llvm-src llvm-project/ \
integration-tests/Or use scripts/docker/test-image.sh with the Docker test image. That script runs unit tests first, then integration tests:
./scripts/docker/test-image.sh # default tag: latest
./scripts/docker/test-image.sh --tag llvm-pr-42 # after scripts/docker/build-image-pr.sh
./scripts/docker/test-image.sh --bind-repo # mount local checkoutBoth --llvm-build and --llvm-sancov-build point at the same SanitizerCoverage tree; --llvm-src is the llvm-project checkout root (used as %llvm-repo in tests).
Tests under integration-tests/e2e/ are marked # REQUIRES: e2e and are not run by default. Pass --param e2e=1 to lit explicitly:
export FUZZ_FILL_E2E_BOOTSTRAP_BIN=/path/to/LLVM-22.1.8/bin
export GH_TOKEN="$(gh auth token)" # or ${{ github.token }} in CI
./integration-tests/test.sh \
--venv ./venv/ \
--llvm-build llvm-project/build-sancov/bin/ \
--llvm-sancov-build llvm-project/build-sancov/bin/ \
--llvm-src llvm-project/ \
--param e2e=1 \
-v integration-tests/e2e/gap-finding-pr/E2E tests use scripts/prepare-pr-llvm.sh with --plain-clone (same squash + self-contained export as the Docker PR path), then build SanitizerCoverage and run gap finding, gap filling, and llvm-reduce on the pinned gap-fill fixture. The first run can take several hours (LLVM build + amdgpu LIT slice). The reduction step checks that reduced/reduced.ll exists and is smaller than the disassembled input (not a byte-for-byte golden match).
fuzz-fill targets two kinds of coverage gap: lines already in LLVM that tests miss, and lines introduced or changed in a PR that tests still miss after the PR's own tests run.
Baseline coverage gaps — lines already present in LLVM that the LIT suite did not exercise. Identified with baseline gap finding and closed with new or expanded LIT tests.
| Date | Commit | Summary |
|---|---|---|
| 2026-03-10 | 30f13b12a0be | New vgpr-mark-last-scratch-load.ll coverage for AMDGPUMarkLastScratchLoad (#185430) |
| 2026-03-19 | c63ce62f7cf6 | New cases in si-lower-i1-copies.mir for SILowerI1Copies (#186127) |
| 2026-03-31 | 67d4842910b8 | New cases in si-lower-sgpr-spills.mir for SILowerSGPRSpills (#189426) |
| 2026-06-12 | 4a3946fc690c | Expanded float-sopc-vopc.ll coverage for SIInstrInfo (#200414) |
| Date | Commit | Summary |
|---|---|---|
| 2026-03-11 | e45c8b6555c8 | New icmp.ll cases for the SPIR-V backend (#185686) |
| 2026-03-17 | b2442a20a946 | New icmp.ll cases for SPIRVInstructionSelector (#186069) |
| 2026-03-25 | 741eb8015253 | New SPIRVEmitIntrinsics.ll for the SPIRVEmitIntrinsics pass (#188285) |
| 2026-03-27 | 294dc1b89452 | New SPIRVEmitIntrinsics-get-element-ptr.ll (#188962) |
| 2026-03-27 | 9238b0f765ad | New SPIRVEmitIntrinsics-infer-ptr-type.ll (#188950) |
| 2026-03-31 | a839e500e8a1 | New SPIRVEmitIntrinsics-infer-fnptr-todo-type.ll (#189413) |
Lines added or changed in LLVM pull requests that baseline coverage (including tests in the PR) still does not hit. Identified with PR gap finding and reported as review feedback on the PR.
| Date | PR | Summary |
|---|---|---|
| 2026-08-04 | #211465 | [AMDGPU] TFE D16 format buffer loads |
| 2026-08-04 | #212755 | [AMDGPU] Constant folding for wave-reduce intrinsics |
| 2026-08-04 | #213202 | [AMDGPU] Negated f16 and fp conversion DAG combines |
| 2026-08-04 | #212507 | [AMDGPU] FLAT_SCRATCH in SILoadStoreOptimizer |
| 2026-08-04 | #212536 | [AMDGPU] MachinePipeliner support |
| 2026-08-04 | #212539 | [AMDGPU] Register pressure of pipelined loops |
| 2026-08-05 | #212305 | [AMDGPU] Fold fsub into fma_mix via free neg_lo modifier |
| 2026-08-21 | #217396 | [AMDGPU] Guard RewriteMFMAFormStage recolor against unsafe def/use |
| 2026-08-25 | #211937 | [AMDGPU] Unpack V_PK_MOV_B32 for MFMA co-issue |
| 2026-08-27 | #218955 | [AMDGPU] Use s_fmamk_f32 when the folded addend is inlinable |
| Date | PR | Summary |
|---|---|---|
| 2026-08-05 | #212999 | [SPIRV] Legalize byte-buffer reinterpretation ptrcasts |
| 2026-08-05 | #213986 | [OpenCL][Clang] Add support for cooperative matrix extension |
| 2026-08-25 | #215794 | [SPIR-V] Preserve constexpr vectors as OpSpecConstantComposite |
| 2026-08-27 | #217965 | [SPIR-V] Add support for DebugLexicalBlock |