Chapter 12 · Passes, Pass Managers & Testing Compilers¶
Part 3 · Analysis Foundations & SSA · about 2–3 weeks · Previous: Ch 11 · Next: Ch 13
The problem¶
Every chapter from here to Chapter 24 writes passes: functions from programs to programs, run in a pipeline that a pass manager schedules, consulting analyses it caches and invalidates. The first half of this chapter asks how that machinery works and must work: given a pipeline \(p_1 \cdots p_k\) over IR units (modules, call-graph SCCs, functions, loops) and a set of analyses with dependencies, run each pass with coherent analysis results, compute each result as rarely as possible, let users add passes without rebuilding the compiler, and decide in which order passes should run. The second half asks how to know that a pass is right: given a compiler or pass \(C\), find inputs on which it crashes or miscompiles, turn them into small reproducible tests, locate the responsible pass or commit, and — where possible — prove that a particular run was correct. The inputs of this half are a compiler and a budget; the outputs are tests with oracles (FileCheck patterns, snapshots, reference executions, equivalent variants, refinement proofs), minimal failing inputs, and culprits. In pebblec, the chapter's passes live in the course plugin PebblePasses (pebble/lib/Passes/Basics/), and every later chapter tests its passes with the lit + FileCheck + lli methodology taught here.
What you will be able to do¶
- Predict which analyses LLVM's new pass manager computes and invalidates for a pipeline, from the
PreservedAnalysesof each pass and the dependencies of each analysis, and prove when caching is sound (drillpm-invalidation). - Compare LLVM's legacy and new pass managers, GCC's property/TODO manager and MLIR's nested operation-anchored manager, and read
-debug-pass-manager,-fdump-passesand-mlir-print-ir-after-alloutput. - Write an analysis with a printer, a transformation that reports precise
PreservedAnalyses, and an instrumentation pass, and register them in a plugin (pebble-stats,pebble-strength,pebble-bbcount); prove whysdiv x, 2^kneeds a bias and whennswsurvives. - Explain how
-O1/-O2/-O3pipelines are assembled and extended, and why no fixed phase order is optimal. - Predict FileCheck's verdict on a check file, including the greedy CHECK-NEXT/CHECK-DAG/CHECK-NOT surprises (drill
filecheck-match); write lit tests with must-not-transform negatives andlliequivalence. - Test the same property as FileCheck, snapshot and differential tests and measure which planted bugs each detects; explain what Csmith, YARPGen, llvm-stress, grammar fuzzers, libFuzzer and EMI add.
- Implement ddmin, prove 1-minimality and its test bound, trace it by hand (drill
ddmin-trace), and compare it with C-Reduce andllvm-reduce; bisect a pipeline with-opt-bisect-limitand a history withgit bisect. - State refinement with undef/poison, check rewrites by exhaustion or SMT (drill
peephole-verify), and validate compiler runs with Alive2.
Prerequisites: Ch 9 (LLVM IR, poison and flags in Lesson 9.7), Ch 10 (the C++ API, IRBuilder, PreservedAnalyses in passing), Ch 2 (grammars, for Lesson 12.6). Chapter 11 is not needed.
Notation¶
Shared notation follows the house notation: §1 (sets, functions, logic), §3 (graphs), §5 (grammars, Lesson 12.6) and §8 (complexity). In this chapter:
| Symbol | Meaning |
|---|---|
| \(\mathcal{P}\), \(P\), \(u\) | the set of programs, a program, an IR unit (module, SCC, function, loop) (Definition 12.1.1) |
| \(A(P, u)\), \(A \to B\) | analysis result on a unit; \(A\) depends on \(B\) (Definition 12.1.2) |
| \(\mathrm{PA} = (K, S, X)\), \(\mathrm{all}\), \(\mathrm{none}\) | preservation report: preserved analyses, preserved sets, abandoned analyses (Definition 12.1.3) |
| \(\mathrm{inv}_A\), \(\mathrm{Inv}\) | invalidation predicate of \(A\); the memoizing invalidator (Definition 12.1.4) |
| \(\Phi\) | GCC's set of IR properties (Proposition 12.1.20) |
| \(\Sigma\), \(w \in \Sigma^{*}\), \(w(P)\), \(m\) | passes, a pass sequence, its result on \(P\), a cost (Definition 12.2.6) |
iN, \(\lfloor x \rfloor_u\), \(\lfloor x \rfloor_s\) |
an \(N\)-bit integer type and a bit string read unsigned or signed (Definition 12.3.1) |
| \(G^{+}\), \(\mathrm{cnt}(u \to v)\), \(e - n + 1\) | extended CFG with the exit-to-entry edge, edge counts, the number of counters needed (Definition 12.3.5, Theorem 12.3.12) |
| \(\mathit{in}\), \([s, e)\), \(\mathrm{first}(p, a, b)\) | FileCheck input, a match interval, the leftmost match of pattern \(p\) in \([a, b)\) (Definition 12.4.1) |
| \(O(C, x)\) | a test oracle (Definition 12.5.1) |
| \(\llbracket P \rrbracket(i)\) | the (unique) behavior of a well-defined program on input \(i\) (Definition 12.5.4) |
| \(q\), \(N\), \(\delta\) | per-program detection probability, number of programs, failure probability (Theorem 12.5.11) |
| \(\pi_A\), \(\xi\), \(m\), \(f(s)\) | production probabilities, offspring count, mean offspring, generating function (Definition 12.6.1) |
| \(\Phi(x)\), \(\mathcal{C}\) | features of an execution, a fuzzing corpus (Definition 12.6.3) |
| \(\mathrm{cov}_I(P)\) | statements executed on input \(I\) (Definition 12.6.5) |
| \(c\), \(n\), \(\Delta_i\), \(\nabla_i\), \(\mathsf{fail}/\mathsf{pass}/\mathsf{unres}\) | a configuration, the granularity, the \(i\)-th subset and complement, test outcomes (Definitions 12.7.1–12.7.2) |
| \(v_0..v_n\), \(\mathrm{bad}\) | a history and a monotone predicate for bisection (Definition 12.7.6) |
| \(t \sqsupseteq s\), \(\mathsf{UB}\), poison | target refines source; immediate undefined behavior; the poison value (Definitions 12.8.1–12.8.2) |
Numbered statements are N.k.m (chapter, lesson, counter), as in NOTATION.md §9.
Technique map¶
| Family | Techniques (origin) | Lesson |
|---|---|---|
| Pass-manager architectures | LLVM legacy pass manager (AnalysisUsage, static schedule; LLVM 1.x, documented in [LLVM-WLP]); LLVM new pass manager (Carruth, 2014–; default since LLVM 13 [LLVM-NPM]); GCC's pass manager (passes.def, properties, TODO flags [GCC-Int]); MLIR's nested, operation-anchored pass manager ([MLIR-PM]) |
12.1 |
| Pipelines and extensibility | Pass plugins and extension points (PassPlugin, PassBuilder callbacks [LLVM-PassPlugin, LLVM-PB]); -O1/-O2/-O3/-Os pipeline assembly ([LLVM-PBP]); the phase-ordering problem (Cooper, Schielke and Subramanian 1999 [CSS99]; Kulkarni et al. 2006 [KWTD06]) |
12.2 |
| Writing passes | Analysis passes and printers ([LLVM-WNPM]); strength reduction by powers of two (Allen, Cocke and Kennedy 1981 [ACK81]; Granlund and Montgomery 1994 [GM94]; [HD2]); counter-based instrumentation (Knuth; Ball and Larus 1994 [BL94]) | 12.3 |
| Oracle tests | lit ([LLVM-lit]); FileCheck ([LLVM-FileCheck]); automatic check generation, update_test_checks.py ([LLVM-UTC]) |
12.4 |
| Snapshot, differential and random testing | Snapshot/golden testing (Turnt [Turnt], insta [Insta]); differential testing (McKeeman 1998 [McK98]); Csmith (Yang et al. 2011 [YCER11]); YARPGen (Livinskii, Babokin and Regehr 2020 [LBR20]); llvm-stress ([LLVM-Stress]) | 12.5 |
| Fuzzing and metamorphic testing | Grammar-based fuzzing (Hanford 1970 [Han70]; Purdom 1972 [Pur72]; [FB-Grammar]); coverage-guided fuzzing with libFuzzer ([LLVM-LibFuzzer]; Serebryany 2016 [Ser16]); equivalence modulo inputs (Le, Afshari and Su 2014 [LAS14]) | 12.6 |
| Reduction and bisection | Delta debugging, ddmin (Zeller and Hildebrandt 2002 [ZH02]); C-Reduce (Regehr et al. 2012 [RCC+12]); llvm-reduce and bugpoint ([LLVM-Reduce, LLVM-BugpointRedesign]); bisection: -opt-bisect-limit, -print-changed, git bisect ([LLVM-OptBisect, GitBisect]) |
12.7 |
| Translation validation | Translation validation (Pnueli, Siegel and Singerman 1998 [PSS98]); Necula's validator (2000 [Nec00]); Alive (Lopes et al. 2015 [LMNR15]); Alive2 (Lopes et al. 2021 [LLH+21]) | 12.8 |
flowchart LR
L[Legacy PM<br/>declared usage] -->|lazy caching, per-run<br/>PreservedAnalyses| N[New PM]
G[GCC PM<br/>properties + TODOs] -.->|same problem| N
N -->|nest along IR,<br/>parallel| M[MLIR PM]
N --> P[Plugins, extension points,<br/>O-pipelines, phase ordering]
P --> W[Your passes:<br/>printer, strength, counters]
W --> T1[lit + FileCheck<br/>update_test_checks]
T1 -->|check everything| S[Snapshots]
T1 -->|no expected output| D[Differential testing]
D --> R[Csmith, YARPGen,<br/>llvm-stress]
D -->|one compiler| E[EMI]
R -->|grammar only| GF[Grammar fuzzing]
GF -->|coverage feedback| CF[libFuzzer]
D --> DD[ddmin]
DD -->|C-aware passes| CR[C-Reduce]
DD -->|IR-aware deltas| LR[llvm-reduce]
D --> B[Bisection]
D -->|prove the run| TV[Translation validation]
TV --> NE[Necula]
TV --> AL[Alive] --> A2[Alive2]
Who uses what¶
| System | Technique | Notes |
|---|---|---|
| LLVM 23 | New pass manager for the optimizer, legacy manager in llc; PassBuilder pipelines and plugins; lit + FileCheck + update_test_checks.py; -opt-bisect-limit; llvm-reduce, llvm-stress, libFuzzer-based *-fuzzer tools |
Lessons 12.1–12.4, 12.7 |
| GCC 15 | passes.def tree with properties and TODO flags; plugins via PLUGIN_PASS_MANAGER_SETUP; -fdump-passes; DejaGnu test suite; --coverage/gcov |
Lessons 12.1, 12.2 |
| MLIR (Flang, CIRCT, IREE, Triton) | Nested operation-anchored pass managers, dynamic pipelines, crash reproducers, mlir-reduce |
Lesson 12.1 |
| Clang | -fpass-plugin, -fsanitize=fuzzer, -fprofile-generate, -fcoverage-mapping |
Lessons 12.2, 12.3, 12.6 |
| Compiler-testing research tools | Csmith, YARPGen (differential testing), Orion (EMI), C-Reduce (reduction), Alive/Alive2 (translation validation) | Lessons 12.5–12.8 |
| Cornell Bril, Rust ecosystem | Turnt and insta snapshot testing | Lesson 12.5 |
pebblec |
One plugin PebblePasses with self-registering passes; lit/FileCheck/lli tests for every pass; course -O1 = registered steps |
exercises.md |
Comparison¶
One row per technique, the same rows as the lessons' §8 tables (each lesson has the "Choose it when…" paragraphs).
| Technique | Power / precision | Speed | Output / error quality | Implementation effort | Typical use |
|---|---|---|---|---|---|
| LLVM legacy PM | Static, declared preservation: must assume the worst case per pass | Recomputes after every declared non-preserving pass, even when nothing changed | -debug-pass=Structure shows the whole schedule up front |
Low per pass (declare usage); complex manager | LLVM codegen (llc) in LLVM 23; LLVM optimizer before LLVM 13 |
| LLVM new PM | Dynamic, per-run PreservedAnalyses; dependency-aware invalidation (Theorem 12.1.17) |
Lazy; recomputes only what a run invalidated; \(O(\lvert \mathrm{cache} \rvert + d)\) bookkeeping per pass | -debug-pass-manager, -print-changed, pass instrumentation |
Moderate per pass (truthful reports, invalidate for dependent analyses) |
LLVM 23 optimizer, opt, clang, the course plugin |
| GCC PM | Coarse: IR properties and TODO flags instead of per-analysis caching | Static tree; repairs (update SSA, cleanup CFG) on request | -fdump-passes, per-pass dumps -fdump-tree-<pass> |
Low per pass (fill in pass_data) |
GCC's GIMPLE, RTL and IPA pipelines |
| MLIR PM | Per-operation analysis managers; nesting follows the IR; op-agnostic pipelines | Parallel over isolated operations (Proposition 12.1.21) | -mlir-print-ir-after-all, -mlir-timing, crash reproducers |
Moderate: declare anchors, keep passes local | MLIR-based compilers (Flang, CIRCT, IREE, Triton) |
| Pass plugins and extension points | Any pass, at named points of the default pipelines or by name in textual pipelines | dlopen + one callback per name; no LLVM rebuild |
"unknown pass name" if unregistered; print<pebble-passes> lists registrations |
Low: one registration line per pass (course registry) | Research passes, out-of-tree tools (Polly, Enzyme, the course plugin) |
| Optimization pipelines (-O1/-O2/-O3/-Os) | Hand-designed, balanced for code in general; not tuned per program | O1: 100 elements; O2: 119; O3: 122 (§7) | -print-pipeline-passes shows the exact pipeline, re-runnable with -passes= |
High, but done once by compiler developers | Every clang -O<n> compilation |
| Phase ordering (search) | Optimal for the searched program, metric and length bound (Theorem 12.2.13) | Exponential in \(L\) in the worst case; pruned by distinct outcomes | Per-program best sequence | Medium (a driver around the compiler) | Research, auto-tuning, embedded code size; informs fixed pipelines |
| Analysis passes and printers | Exact facts about the current IR; cached per unit | \(\Theta(I + e)\) per computation, reused until invalidated | A documented, FileCheck-able format | Low (analysis + printer + registration) | Every LLVM analysis; the course's print<pebble-x> contracts |
| Strength reduction by powers of two | Exact (Theorems 12.3.9–12.3.10); only power-of-two constants | \(\Theta(I)\); replaces a multi-cycle hardware divide by 1–4 single-cycle instructions | Wrong if done naively (sdiv → ashr, nsw kept at \(k = N-1\)) | Low, but the proofs matter | InstCombine (mul/udiv/urem/exact sdiv), DAGCombiner (sdiv), pebble-strength |
| Counter-based instrumentation | Exact counts of the training runs; spanning tree: minimum counters (Theorem 12.3.12) | One increment per executed block (naive) or per instrumented edge (MST) | Per-block counts for PGO; needs a runtime | Low (naive) to medium (MST + reconstruction) | -fprofile-generate, -fprofile-arcs, gcov, pebble-bbcount |
| lit | Runs any shell pipeline; features select configurations; XFAIL for known bugs | Parallel; per-test cost is the tools | PASS/FAIL/XFAIL/XPASS/UNSUPPORTED with the failing command | Low per test (RUN lines); one lit.cfg.py per suite |
LLVM, Clang, MLIR, this course |
| FileCheck | Ordered patterns with NEXT/SAME/NOT/DAG/LABEL, captures, numeric expressions; greedy, sound but incomplete (Theorems 12.4.9, 12.4.10, Prop. 12.4.11) | \(O(m\,n\,\lvert p \rvert)\); fast | Precise error location and an annotated input dump | Low to write, easy to get subtly wrong (NOT ranges, DAG/NOT ordering) | Checking the relevant part of compiler output |
| Automatic check generation | Checks everything up to consistent renaming (Theorem 12.4.14) | One run per update | Diffs of generated checks in review | Very low per test; one generator per output kind | InstCombine and most IR transformation tests |
| Snapshot testing | Detects every output change (Prop. 12.5.9); false alarm on every benign change | One run + diff per input | A diff to review; accepting is one command | Very low per test | Printers, diagnostics, small IR outputs; update_test_checks |
| Differential testing | Sound only for well-defined programs (Thm. 12.5.10); misses shared and unobservable bugs (Prop. 12.5.12) | \(k\) compilations + runs per program; \(1/q\) programs to a bug | A disagreeing program (large: reduce it) | Low (a harness) given a generator | Csmith/YARPGen campaigns, -O0 vs -O2, before/after a pass |
| Csmith | UB-free C with pointers, structs, unions, volatile; checksum oracle | Seconds per program | Thousands of lines per failing program | High (a large C++ generator with its own pointer and effect analyses) | Finding miscompilations in C compilers |
| YARPGen | UB-free C/C++ with loops and arrays, policies for optimizations | Sub-second generation | Two files per test; hashes of results | High | Loop and vectorizer bugs in GCC/LLVM/ICC |
| llvm-stress | Random verifier-clean IR; crash oracle only | Milliseconds | Small IR functions | Low (one LLVM tool) | Crash testing passes and back ends |
| Grammar-based fuzzing | Syntactically valid inputs; no semantic validity; terminates with cost-aware expansion (Thm. 12.6.7) | Microseconds per input | Readable inputs; crash oracle unless combined with differential testing | Low (a grammar + Algorithm 12.6.2) | Parser and front-end robustness |
| Coverage-guided fuzzing (libFuzzer) | Finds inputs that pass deep checks without knowing the format (Thm. 12.6.8); crash/sanitizer oracle | \(10^5\)–\(10^6\) executions/s in process | A minimal-ish crashing input (-minimize_crash) |
Low: one entry point + -fsanitize=fuzzer |
Parsers, decoders, LLVM's own *-fuzzer tools |
| Equivalence modulo inputs | Detects miscompilations with one compiler and no expected output (Thm. 12.6.9) | A coverage run + one compile/run per variant | A pair of programs that differ only in dead code | Medium (coverage + a source rewriter) | Miscompilation hunting in GCC/LLVM (Orion, Athena, Hermes) |
| Delta debugging (ddmin) | 1-minimal (Thm. 12.7.8), format-agnostic; stuck on structure (paired lines) | \(O(\log N)\) best, \(\le 3N^2 + 3N\) tests worst; lab: 195 tests for 53 lines | Readable only at the chosen granularity | Low (≈100 lines; the lab) | Byte inputs, change sets, line-level first cuts |
| C-Reduce | Fixpoint of C-aware transformations (Thm. 12.7.11); far smaller than ddmin | Minutes to hours; lab: ~100 s | Tiny, idiomatic C (may contain UB) | High (a tool) — use it | Csmith and user reports of C/C++ compiler bugs |
| llvm-reduce (bugpoint) | Fixpoint over IR entities; output always verifies | Seconds to minutes; lab: 8.5 s | Minimal IR tests ready for llvm/test |
Low to use | Any LLVM crash or IR-level miscompile with a script |
| Bisection | Exact first bad state for monotone histories (Thm. 12.7.12) | \(\lceil \log_2 n \rceil\) tests | One pass execution or one commit | Very low | Localizing a pass (-opt-bisect-limit) or a commit (git bisect) |
| Translation validation | Proves each run correct (sound, Theorem 12.8.11); says nothing about unvalidated programs | Solver time per run; ms for peepholes | A concrete counterexample input | Medium: a semantics + an encoding (the course's is ~250 lines) | Checking a pass on its test corpus, bug hunting |
| Necula's validator | Handles control-flow-changing optimizations via inferred simulation relations; incomplete (unknown) | Seconds per function (2000-era GCC) | The failing verification condition | High | GCC's optimizer in [Nec00]; ideas reused in later validators |
| Alive | Proves rewrite rules for all feasible types, with undef/poison; infers flags | Seconds per rule | Counterexample with types and values | Low per rule (the DSL) | InstCombine rewrites, flag questions |
| Alive2 | Whole functions, memory, undef/poison, bounded loops (Prop. 12.8.12) | ms to minutes; timeouts | Counterexample, or "failed to prove" | Very low to use | Validating LLVM passes and tests daily; reviewing patches |
Comparison-lab results (reference solution in the course's Linux container; reproduce with ctest --test-dir build/<preset> -R ch12.lab after L1 and L2, and the commands in Lesson 12.7 §7):
- Three oracles against five planted bugs (Part A,
ch12.lab.matrix): FileCheck detects bugs 1, 2, 3, 5 and correctly passes the harmless bug 4; snapshots detect all five, including a false alarm on bug 4; the differential tester detects 1, 2, 3, passes 4, and misses the poison-only bug 5 (which translation validation catches, Lesson 12.8). - Reducers on the planted crash (Part B, 53-line C file): line-level ddmin keeps 10 lines after 195 tests (≈12 s); token-level ddmin keeps 119 of 205 tokens after 1 653 tests (≈65 s); C-Reduce reaches 4 lines (≈100 s);
llvm-reduceon the 220-line IR reaches a 3-instruction function (≈8.5 s).
Route through this chapter¶
| Step | What | Techniques | How it is exercised |
|---|---|---|---|
| 1 | Lesson 12.1 | legacy PM, new PM, GCC PM, MLIR PM | drill pm-invalidation; Pebble implements pebble-stats (E1); quiz; flashcards |
| 2 | Lesson 12.2 | plugins and extension points, O-pipelines, phase ordering | E2 joins the course -O1; quiz |
| 3 | Lesson 12.3 | analysis printers, strength reduction, counter instrumentation | Pebble implements E1–E3; drill peephole-verify |
| 4 | Lesson 12.4 | lit, FileCheck, update_test_checks | drill filecheck-match; tests/ch12/lit; lab L1 |
| 5 | Lesson 12.5 | snapshots, differential testing, Csmith, YARPGen, llvm-stress | lab Part A (L1): three oracles vs planted bugs |
| 6 | Lesson 12.6 | grammar fuzzing, libFuzzer, EMI | quiz; real-world boxes; ★ E4 (libFuzzer on your parser); Chapter 24's fuzzer |
| 7 | Lesson 12.7 | ddmin, C-Reduce, llvm-reduce, bisection | drill ddmin-trace; lab Part B (L2): your ddmin vs llvm-reduce |
| 8 | Lesson 12.8 | translation validation, Necula, Alive, Alive2 | drill peephole-verify; examples/tv.py |
| 9 | Exercises | Pebble uses the new pass manager through the PebblePasses registry |
./course test 12 |
| 10 | Comparison lab labs/ch12-testing/ |
FileCheck vs snapshot vs differential; ddmin vs llvm-reduce (vs ★ C-Reduce) | lab tests + measurements |
| 11 | Theory test | all | ./course quiz 12 (≥ 80 % to finish) |
Practice and check¶
./course drill pm-invalidation --difficulty easy # which analyses are computed and invalidated
./course drill filecheck-match # FileCheck's verdict and match lines
./course drill ddmin-trace # ddmin rounds, result, test count
./course drill peephole-verify # is this rewrite correct? (exhaustive i4)
./course flash 12 # daily, a few minutes
./course quiz 12 # after the lessons
./course test 12 # after the exercises and the lab
./course status # done = quiz ≥ 80 % and tests pass
Tools outside the course toolchain¶
Most real-world boxes need only the course toolchain (clang, opt, llc, lli, llvm-reduce, llvm-stress, FileCheck, lit
23.1.2) and the reference build of ./course test 12 --solution (SOL=$PWD/build/ci-solutions-linux, or
build/ci-solutions-macos). The others need these tools; the boxes' outputs came from the versions listed.
| Tool (boxes) | macOS | Linux |
|---|---|---|
| compiler-rt 23.1.2: profile, fuzzer and sanitizer runtimes (Lessons 12.3, 12.6) | Homebrew's llvm ships them; omit -resource-dir="$RD" |
sudo apt install libclang-rt-23-dev with apt.llvm.org's clang-23 (omit -resource-dir), or with the course's conda-forge toolchain build a resource directory $RD as below |
| mlir-opt 23.1.2 (Lesson 12.1) | Homebrew's llvm ($(brew --prefix llvm)/bin/mlir-opt) |
apt.llvm.org: sudo apt install mlir-23-tools; or conda-forge mlir=23.1.2, run with its lib/ on LD_LIBRARY_PATH |
| gcc 14.2.0 (Lessons 12.1, 12.5) | brew install gcc@14 (driver gcc-14) |
Ubuntu 24.04: sudo apt install gcc-14 |
| Csmith 2.3.0 (Lesson 12.5) | brew install csmith; headers in $(brew --prefix csmith)/include/csmith-2.3.0 |
Ubuntu 24.04: sudo apt install csmith libcsmith-dev; headers in /usr/include/csmith |
YARPGen 2.0 at e2a0512 (Lesson 12.5) |
build from source as below | build from source as below |
| C-Reduce (Lesson 12.7) | brew install creduce (2.10.0) |
Ubuntu 24.04: sudo apt install creduce (2.11.0, used here) |
Alive2 at 01a5ec45: alive, alive-tv (Lesson 12.8) |
build from source as below, with brew install z3 re2c ninja |
build from source as below, with sudo apt install libz3-dev re2c ninja-build |
| Turnt, fuzzingbook, z3-solver (Lessons 12.5, 12.6, 12.8) | uvx turnt, uvx --from fuzzingbook, uv run --with z3-solver |
same |
The resource directory with compiler-rt for the conda-forge toolchain (Linux x86-64):
curl -sSLO https://conda.anaconda.org/conda-forge/noarch/compiler-rt23_linux-64-23.1.2-h0e38de2_0.conda
mkdir -p crt && (cd crt && unzip -q ../compiler-rt23_linux-64-23.1.2-h0e38de2_0.conda && tar --zstd -xf pkg-*.tar.zst)
RD=$PWD/rd && mkdir -p "$RD" && cp -r "$(clang-23 -print-resource-dir)"/. "$RD" && cp -r crt/lib/clang/23/lib "$RD"/
YARPGen and Alive2 from source (any platform; LLVM_DIR is your LLVM 23.1.2's lib/cmake/llvm, e.g.
$(brew --prefix llvm)/lib/cmake/llvm or /opt/llvm-23/lib/cmake/llvm):
git clone https://github.com/intel/yarpgen && cd yarpgen && git checkout e2a0512
sed -i.bak 's/ -Werror>/>/' src/CMakeLists.txt # newer clang warns where YARPGen's -Werror stops the build
cmake -B build -DCMAKE_CXX_COMPILER=clang++ && cmake --build build # -> build/yarpgen
cd .. && git clone https://github.com/AliveToolkit/alive2 && cd alive2 && git checkout 01a5ec45
cmake -GNinja -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TV=1 -DLLVM_DIR="$LLVM_DIR" && cmake --build build # -> build/alive, build/alive-tv
Alive2 is pinned because it tracks LLVM's development branch: a newer commit may not build against LLVM 23.1.2.
References¶
The chapter's annotated bibliography — papers, textbook sections, pinned source files and docs — is in references.md. Start with: [LLVM-NPM] (the pass manager you use), [LLVM-FileCheck] and [LLVM-TestingGuide] (how every later test is written), [YCER11] and [McK98] (random differential testing), [ZH02] (delta debugging), and [LLH+21] (Alive2).