CLI tool
EnzymeAD/Enzyme avatar
EnzymeAD/Enzyme

Enzyme: Automatic Differentiation as an LLVM and MLIR Pass

High-performance automatic differentiation of LLVM and MLIR.

1,694 stars188 forksLLVMNOASSERTION

At a glance

What is it?
Enzyme is a compiler plugin that differentiates statically analyzable LLVM IR and MLIR rather than source code. It suits people already inside an LLVM toolchain; the build and licence terms are the first things to check.
Who is it for?
Enzyme fits teams that already build LLVM or MLIR based compilers, language runtimes, or HPC kernels and want gradients without rewriting source. It is the wrong tool if you cannot pin an LLVM revision, if your code is dynamically typed in ways that defeat static analysis, or if you only need gradients for a Python model that PyTorch already covers.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly LLVM, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem Enzyme solves is that gradients usually require rewriting the program

Most automatic differentiation tools ask you to express your computation in their language or their operator set. If your code is a C++ simulation kernel, a Fortran solver, or a Rust library, that means a port, a wrapper layer, or a hand-written adjoint. Enzyme takes the opposite position: it operates on statically analyzable LLVM and MLIR, so the program can stay in its original form. The README's example is three lines of C. You declare a function, write a second function that calls __enzyme_autodiff on it, and the Enzyme transformation pass replaces that call with the gradient of its first argument. There is no tape object to construct at the call site and no custom type to thread through the code. The target audience follows from that design: compiler engineers, HPC groups with existing C, C++ or Fortran codes, and language implementors who want a differentiation backend. The README lists Julia, Rust and Fortran bindings, which indicates the intended path for users who do not want to touch LLVM IR directly.

How the pass turns an LLVM function into its derivative

Enzyme is described as a plugin, which means it runs as part of an existing LLVM or MLIR pipeline rather than as a standalone binary that reads your source. The mechanism is a marker call. __enzyme_autodiff is not a real function; it is a placeholder the pass recognizes. When the transformation runs, it finds that call, analyzes the function passed as its first argument, and emits differentiated code in its place. Because this happens after the front end has lowered your source to IR, Enzyme sees the program as the optimizer sees it. The README makes a performance argument from exactly this: the ability to differentiate optimized code is what lets Enzyme, in the project's own words, meet or exceed the performance of state-of-the-art AD tools. That claim is the project's, not an independent measurement, and the README supports it with a chart rather than a reproducible benchmark in the text. The important architectural point is that differentiation is a compiler transformation over IR, so the input has to be statically analyzable for the pass to succeed. Anything that hides the data flow from static analysis is a candidate for failure.

Building Enzyme against your own LLVM tree

The README gives a short build recipe. From the enzyme subdirectory of the checkout, create a build directory, then run cmake with Ninja as the generator, pointing LLVM_DIR at your LLVM installation's lib/cmake/llvm directory and LLVM_EXTERNAL_LIT at a lit.py. Then run ninja. The dependency on LLVM_DIR is the constraint that matters most in practice: Enzyme is built against a specific LLVM, and the build will not silently work against an arbitrary version. If you are not building from source, the README lists three package managers. Homebrew installs with brew install enzyme. Spack installs with spack install enzyme. Nix provides it through nix-shell -p enzyme. The website at enzyme.mit.edu is where the README directs readers for detailed installation and usage information, so the repository README is a starting point rather than the full manual. For Fortran users, the README points at a Fortran directory inside the enzyme tree. For Julia and Rust, it points at separate repositories, Enzyme.jl and EnzymeAD/rust, which means the binding version and the plugin version are separate things to track.

What Enzyme cannot differentiate

The README's own qualifier is the limitation: Enzyme performs AD of statically analyzable LLVM and MLIR. That phrase rules out a large class of programs. If a value's path through the computation depends on something the compiler cannot resolve, such as a function pointer chosen at runtime, a dynamically dispatched method, or an external library call whose body is not visible, the pass has nothing to differentiate through. This is a different failure mode from a source-level AD tool, which can often fall back to operator overloading and record whatever actually executes. Enzyme's static requirement is also why the marker-call interface matters: you are asserting to the compiler that the function is a suitable target. A second practical constraint is version coupling. The repository shows frequent releases, with v0.0.293, v0.0.292 and v0.0.291 appearing within roughly two weeks of each other, and the version numbering stays in the 0.0.x range. Anyone pinning Enzyme in a build system should expect to move with LLVM rather than against it. Finally, the README does not describe error behavior when differentiation fails. There is no documented list of unsupported constructs in the supplied material, so the only reliable way to know whether your kernel qualifies is to try the pass on it.

Enzyme versus source-transformation tools such as Tapenade

The closest comparison in kind is Tapenade, which differentiates Fortran and C by transforming source code rather than compiler IR. The difference in approach shows up in what each tool can see. Tapenade works on the program text, so it is portable across compilers and does not care which LLVM version you have, but it cannot benefit from optimizations the compiler has already applied, and it must reconstruct control flow and aliasing from the source. Enzyme sits after the front end and the optimizer, so it differentiates the code that will actually run, which is the basis for its performance argument. The cost is that Enzyme is tied to LLVM, and therefore to a compiler revision, in a way Tapenade is not. Operator-overloading libraries such as Adept are a third position: they differentiate by recording operations at runtime through a custom numeric type, which handles dynamic control flow that Enzyme's static analysis would reject, at the price of changing your data types and paying recording overhead. None of these is strictly better. The choice is between portability across compilers, tolerance of dynamic behavior, and differentiation of optimized IR.

Maintenance, releases and what the licence field does not tell you

The release cadence is fast. Three tagged releases in the two weeks before the last recorded push, all in the 0.0.x series, is the pattern of a project that has not declared a stable interface. For a build that vendors Enzyme, that means the upgrade cost is real and recurring: each bump may require a matching LLVM and possibly a matching binding release for Julia, Rust or Fortran. The repository metadata reports the licence as NOASSERTION, which means GitHub could not classify the licence file automatically. That is not a statement that the project is unlicensed, and it is not a statement that it is permissive. It means the terms have to be read directly in the repository before you depend on Enzyme in a product. The README also asks academic users to cite three papers, one for Enzyme as a whole, one for GPU and optimization work, and one for differentiation of parallel programs including OpenMP, MPI and Julia tasks. That citation request is a norm for academic use, not a licence condition, but it tells you where the project's development effort has been concentrated.

Where Enzyme is the right call and where it is not

Adopt Enzyme if your computation already passes through LLVM or MLIR and you want gradients without a source rewrite. That covers compiler projects adding differentiation to a language, HPC codes where the adjoint is the bottleneck, and GPU kernels, given the project's published work on reverse-mode differentiation of CUDA and ROCm kernels. Do not adopt it if your differentiation target is a Python training loop that PyTorch or TensorFlow already handles, or if your code relies on runtime dispatch that static analysis cannot resolve. If you are evaluating it, the first thing to verify is the licence file in the repository, because the NOASSERTION classification leaves the terms genuinely unclear from metadata alone. The second is the LLVM version your build expects, since the cmake invocation requires an explicit LLVM_DIR and there is no documented compatibility matrix in the supplied material. The third is whether the binding for your language is maintained at the same cadence as the plugin; the Julia, Rust and Fortran entry points live outside this repository or in a subdirectory of it, and their release timing is not the same as the v0.0.x tags.

Editorial conclusion

Enzyme fits teams that already build LLVM or MLIR based compilers, language runtimes, or HPC kernels and want gradients without rewriting source. It is the wrong tool if you cannot pin an LLVM revision, if your code is dynamically typed in ways that defeat static analysis, or if you only need gradients for a Python model that PyTorch already covers. Before adopting, resolve the repository licence file (GitHub reports NOASSERTION), check which LLVM version the current release tag expects, and confirm that your language has a maintained binding rather than a stale one.

Official sources

  1. EnzymeAD/Enzyme on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Community notes

Community notes