Enzyme: automatic differentiation inside LLVM and MLIR
High-performance automatic differentiation of LLVM and MLIR.
At a glance
- What is it?
- Enzyme is a compiler plugin that differentiates statically analyzable LLVM and MLIR code, so gradients come from the optimizer's view of a program rather than from a tracing or operator-overloading library. It is aimed at people who already build with LLVM and want derivatives of code the compiler can see.
- Who is it for?
- Enzyme fits teams that already compile through LLVM or MLIR and need derivatives of code the compiler can analyze, including GPU kernels and parallel programs, and who are willing to track a fast-moving release line. It is the wrong tool if your model is a dynamic Python graph you cannot lower to LLVM, or if you need a supported, versioned API with a documented rollback path.
- 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 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Enzyme solves: gradients without rewriting the program
Most automatic differentiation in machine learning happens at a level above the compiler. A framework records operations, builds a graph, and differentiates that graph. Enzyme takes a different position: it is described in the README as a plugin that performs automatic differentiation of statically analyzable LLVM and MLIR. The input is not a model definition but the intermediate representation the compiler already has.
That matters for code that never passed through a tensor framework. Simulation kernels, numerical solvers, and hand-written C or C++ routines can be differentiated without being rewritten as framework operations. The README frames the same idea in the title of its NeurIPS citation: instead of rewriting foreign code for machine learning, automatically synthesize fast gradients. The intended audience is therefore compiler engineers, scientific computing groups, and anyone whose hot loop is already compiled code.
The README also claims that performing AD on optimized code lets Enzyme meet or exceed the performance of state-of-the-art AD tools. That is a claim from the project, not an independent measurement, and the README does not publish the benchmark methodology behind the accompanying plot.
How the __enzyme_autodiff call becomes a gradient
The mechanism is a function call that the transformation pass replaces. The README gives this example:
double foo(double);
double grad_foo(double x) {
return __enzyme_autodiff(foo, x);
}The call to __enzyme_autodiff is a marker. The README states that running the Enzyme transformation pass replaces the call with the gradient of its first argument. So the flow is: you write ordinary code, mark the function whose derivative you want, and the pass rewrites the marked call site into the differentiated version. Because the pass runs over LLVM IR, it sees the program after optimization, which is the property the project leans on for performance.
The same design extends beyond scalar C. The README lists Julia, Rust, and Fortran bindings, and its citations cover reverse-mode differentiation of GPU kernels and differentiation of parallel programs including OpenMP, MPI, and Julia tasks. Those are compiler-level targets: the differentiation happens on the IR for those constructs rather than on a host-side graph. The README does not document the pass pipeline order, the exact flags, or which IR constructs are rejected, so treat the pass invocation details as something to confirm on enzyme.mit.edu.
Installing Enzyme and differentiating a first function
The README points to https://enzyme.mit.edu for detailed installation and usage information, and gives a short CMake build against an existing LLVM tree. The commands below are copied from the README, with the paths left as placeholders. LLVM_DIR must point at your LLVM's CMake package directory, and LLVM_EXTERNAL_LIT at your lit runner.
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
ninjaIf you would rather not build from source, the README lists three package managers. Each installs a package named enzyme.
brew install enzymespack install enzymenix-shell -p enzymeAfter installation, the first real use follows the README's own example: declare a function, then mark the call site whose derivative you want.
double foo(double);
double grad_foo(double x) {
return __enzyme_autodiff(foo, x);
}Compile that translation unit with your LLVM-based toolchain and run the Enzyme pass. What you should see is the __enzyme_autodiff call gone from the emitted code, replaced by the differentiated computation. The README does not spell out the exact pass flag or the command line for the pass itself; that is documented on the project website, so check there before assuming a flag name.
The static-analysis boundary is the real limitation
Enzyme differentiates statically analyzable code. That phrase in the README is doing a lot of work, and it is the constraint that decides whether the project fits your problem. Code the compiler cannot see through, such as calls into opaque binaries, dynamically dispatched behavior it cannot resolve, or data structures whose layout is not visible in the IR, sits outside what the pass can reason about. The README does not enumerate the failure modes or list which constructs are unsupported, which is a gap: a user hitting a rejection has no documented taxonomy to consult.
There is a second cost that follows from the design. Enzyme is a plugin against LLVM and MLIR, so your LLVM version is part of your dependency surface. A plugin built against one LLVM tree is not a drop-in for another. The README's build instructions take LLVM_DIR as an argument precisely because the build is tied to a specific LLVM installation. Teams that pin their compiler for other reasons will need to keep Enzyme in step with that pin.
The project is also on a fast release cadence. The most recent releases listed are v0.0.293 on 2026-09-10, v0.0.292 on 2026-09-04, and v0.0.291 on 2026-08-25. Three releases in roughly two weeks, all on a 0.0.x version line, tells you the interface is not being held stable. The last push to the repository was on 2026-09-10.
Enzyme compared with source-transformation AD tools
The closest alternative approach is a source-to-source or operator-overloading AD tool that works on the language level rather than on IR. Tools in that family parse or instrument your program and emit new source or new operations, which means they see the program as written, before the compiler has optimized it. Enzyme sees the program as the optimizer left it.
The practical difference shows up in what each tool can reach. A source-level tool can differentiate code that never goes through LLVM, including interpreted or dynamically typed programs, and it usually offers a stable library API. Enzyme cannot do that; its reach stops at statically analyzable LLVM and MLIR. In exchange, Enzyme can differentiate compiled kernels and parallel constructs that a source-level tool would have to be taught about individually, which is why the project's own papers target GPU kernels and OpenMP, MPI, and Julia task parallelism.
So the choice is not about which tool is better in the abstract. If your computation is a Python graph you cannot lower to LLVM, a source-level tool is the right shape. If your computation is already compiled and you want the derivative of the optimized form, Enzyme is the tool built for that case. The README does not offer a migration path between the two, and no compatibility layer is described.
Maintenance, release cadence, and what the licence field does not tell you
The repository is not archived, and the last push was on 2026-09-10, so the code line is moving. The release history backs that up: v0.0.291, v0.0.292, and v0.0.293 arrived within about two weeks of each other. Frequent 0.0.x releases are a reasonable sign of active work, but they are also a warning about upgrade cost. There is no stable major version to anchor against, and the README does not document a deprecation policy, a compatibility guarantee, or a rollback procedure for a release that breaks your build.
Upgrading therefore means rebuilding the plugin against your LLVM tree and re-running your own differentiation tests. The README does not describe a test suite you can run to check a new release against your code, so the verification burden lands on you.
On licensing, the repository's licence identifier is reported as NOASSERTION, which means no recognized SPDX identifier was detected. The repository does contain a LICENSE file at the top level. If you plan to redistribute Enzyme or ship it inside a product, read that file directly rather than relying on the identifier, and get your own legal review. The README itself only asks that academic users cite three papers, which is a citation request rather than a licence term.
Editorial conclusion
Enzyme fits teams that already compile through LLVM or MLIR and need derivatives of code the compiler can analyze, including GPU kernels and parallel programs, and who are willing to track a fast-moving release line. It is the wrong tool if your model is a dynamic Python graph you cannot lower to LLVM, or if you need a supported, versioned API with a documented rollback path. Before adopting it, check which LLVM version your toolchain uses against the build instructions on enzyme.mit.edu, and read the LICENSE file in the repository, since the license identifier is reported as NOASSERTION rather than a recognized SPDX tag.
Frequently asked questions
What is Enzyme and what does it differentiate?
Enzyme is a plugin that performs automatic differentiation of statically analyzable LLVM and MLIR, according to the README. You mark a function with __enzyme_autodiff, and running the Enzyme transformation pass replaces that call with the gradient of its first argument.
How do I install Enzyme?
The README gives a CMake and Ninja build inside the enzyme directory that takes LLVM_DIR and LLVM_EXTERNAL_LIT as arguments, and also lists Homebrew, Spack, and Nix, each installing a package named enzyme. Detailed installation instructions are on https://enzyme.mit.edu.
Does Enzyme bind to languages other than C and C++?
The README states that Julia, Rust, and Fortran bindings are available, and its citations cover differentiation of GPU kernels and of parallel programs including OpenMP, MPI, and Julia tasks.
What LLVM version does Enzyme need?
The README does not state a supported LLVM version range. Its build command points LLVM_DIR at a specific LLVM CMake package directory, so the build is tied to whichever LLVM tree you supply, and the project website is the place the README directs you to for details.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/enzymead-enzyme)