Keep the explicit cache decision for a loopy reduction's start value

computeMinCache runs a min cut over Intermediates, then marks everything in
that set as recomputed or cached according to which side of the cut it landed
on:

  knownRecomputeHeuristic[V] = !MinReq.count(V);

Membership of Intermediates therefore carries an invariant, established where
the set is built: a value only joins it if it is legal to recompute, since
anything not selected by the cut will be. Values which are not legal to
recompute are excluded there, having already been given an explicit
knownRecomputeHeuristic[V] = false by the scan above.

pushLoopyPHIPreheader broke that invariant. Added in 04b80f67 to pull a loopy
reduction's start value into the need graph, it inserted straight into
Intermediates -- unconditionally, and after the cut had already been computed,
so the value could never be selected for caching. For a start value that is
not legal to recompute, that overwrote a correct "cache this" decision with
"recompute this", and computeMinCache then caught its own inconsistency:

  Assertion `legalRecompute(V, Available2, nullptr)' failed.

In the reported case the start value is a load whose memory may be written
before the reverse pass runs, so it must be cached.

Have pushLoopyPHIPreheader collect the values instead, and let computeMinCache
apply the same legality gate the main worklist uses. A value that passes joins
Intermediates as before; one that does not keeps the decision already made for
it. Either way it is pushed onto the worklist, so the need graph is unchanged.

Fixes #3040

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P65yn8LELKWU1dq6AQvA5f
4 files changed
tree: 77be82478f1d84e547153e02e6e113a8c9c55441
  1. .devcontainer/
  2. .github/
  3. .packaging/
  4. enzyme/
  5. integration/
  6. .gitattributes
  7. .gitignore
  8. CITATION.bib
  9. CONTRIBUTING.md
  10. LICENSE
  11. Readme.md
Readme.md

The Enzyme High-Performance Automatic Differentiator of LLVM and MLIR

Enzyme is a plugin that performs automatic differentiation (AD) of statically analyzable LLVM and MLIR.

Enzyme can be used by calling __enzyme_autodiff on a function to be differentiated as shown below. Running the Enzyme transformation pass then replaces the call to __enzyme_autodiff with the gradient of its first argument.

double foo(double);

double grad_foo(double x) {
    return __enzyme_autodiff(foo, x);
}

Enzyme is highly-efficient and its ability to perform AD on optimized code allows Enzyme to meet or exceed the performance of state-of-the-art AD tools.

Detailed information on installing and using Enzyme can be found on our website: https://enzyme.mit.edu.

A short example of how to install Enzyme is below:

cd /path/to/Enzyme/enzyme
mkdir build && cd build
cmake -G Ninja .. -DLLVM_DIR=/path/to/llvm/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=/path/to/lit/lit.py
ninja

Or, install Enzyme using a package manager:

Homebrew

brew install enzyme

Spack

spack install enzyme

Nix

nix-shell -p enzyme

To get involved or if you have questions, please join our mailing list.

If using this code in an academic setting, please cite the following three papers (first for Enzyme as a whole, second for GPU+optimizations, and third for AD of all other parallel programs (OpenMP, MPI, Julia Tasks, etc.)):

@inproceedings{NEURIPS2020_9332c513,
 author = {Moses, William and Churavy, Valentin},
 booktitle = {Advances in Neural Information Processing Systems},
 editor = {H. Larochelle and M. Ranzato and R. Hadsell and M. F. Balcan and H. Lin},
 pages = {12472--12485},
 publisher = {Curran Associates, Inc.},
 title = {Instead of Rewriting Foreign Code for Machine Learning, Automatically Synthesize Fast Gradients},
 url = {https://proceedings.neurips.cc/paper/2020/file/9332c513ef44b682e9347822c2e457ac-Paper.pdf},
 volume = {33},
 year = {2020}
}
@inproceedings{10.1145/3458817.3476165,
author = {Moses, William S. and Churavy, Valentin and Paehler, Ludger and H\"{u}ckelheim, Jan and Narayanan, Sri Hari Krishna and Schanen, Michel and Doerfert, Johannes},
title = {Reverse-Mode Automatic Differentiation and Optimization of GPU Kernels via Enzyme},
year = {2021},
isbn = {9781450384421},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3458817.3476165},
doi = {10.1145/3458817.3476165},
booktitle = {Proceedings of the International Conference for High Performance Computing, Networking, Storage and Analysis},
articleno = {61},
numpages = {16},
keywords = {CUDA, LLVM, ROCm, HPC, AD, GPU, automatic differentiation},
location = {St. Louis, Missouri},
series = {SC '21}
}
@inproceedings{10.5555/3571885.3571964,
author = {Moses, William S. and Narayanan, Sri Hari Krishna and Paehler, Ludger and Churavy, Valentin and Schanen, Michel and H\"{u}ckelheim, Jan and Doerfert, Johannes and Hovland, Paul},
title = {Scalable Automatic Differentiation of Multiple Parallel Paradigms through Compiler Augmentation},
year = {2022},
isbn = {9784665454445},
publisher = {IEEE Press},
booktitle = {Proceedings of the International Conference on High Performance Computing, Networking, Storage and Analysis},
articleno = {60},
numpages = {18},
keywords = {automatic differentiation, tasks, OpenMP, compiler, Julia, parallel, Enzyme, C++, RAJA, hybrid parallelization, MPI, distributed, LLVM},
location = {Dallas, Texas},
series = {SC '22}
}

Julia bindings, Rust bindings, and Fortran bindings are available for Enzyme.