# Enzyme.jl: LLVM-Level Automatic Differentiation for Julia

> Enzyme.jl provides Julia bindings to the Enzyme compiler plugin, which performs automatic differentiation directly on optimized LLVM intermediate representation rather than at the Julia source level. The result is a tool that can differentiate GPU kernels and low-level numerical code that source-level AD frameworks cannot reach, with derivative performance that the documentation states can match or exceed purpose-built AD tools.

**EnzymeAD/Enzyme.jl** — Julia bindings for the Enzyme automatic differentiator

- Repository: https://github.com/EnzymeAD/Enzyme.jl
- Website: https://enzymead.github.io/Enzyme.jl/
- Stars: 589 · Forks: 111
- Language: Julia
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/enzymead-enzyme-jl

## What Enzyme.jl Solves and Who It Is For

Enzyme.jl is the Julia interface to Enzyme, a compiler plugin that performs automatic differentiation (AD) on LLVM intermediate representation. The project targets Julia numerical computing researchers, machine learning engineers, and scientists who need gradients of arbitrary Julia code, including code that runs on GPU hardware. Standard Julia AD frameworks operate at or above the Julia source level, which means they see a high-level view of the computation and must handle language constructs one by one. Enzyme takes a different route: it reads the same LLVM IR that the compiler would otherwise emit to hardware and synthesizes the derivative program at that layer.

The practical consequence is that Enzyme.jl can reach code that source-level tools cannot differentiate without special casing. GPU kernels written with Julia's GPU stack compile to LLVM IR before being lowered to device code, and Enzyme can differentiate them at that stage. The README states that Enzyme's ability to perform AD on optimized code allows it to meet or exceed the performance of state-of-the-art AD tools. That claim is specific to the mechanism: derivative code inherits the loop fusions, inlinings, and scalar optimizations the compiler already applied to the original, so the backward pass does not see a slower, pre-optimization view of the computation.

## How Enzyme Differentiates at the LLVM Layer

When you call autodiff on a Julia function, the Julia compiler lowers that function to LLVM IR, runs its standard optimization passes, and Enzyme then reads the resulting IR to synthesize the derivative. The derivative program is also an LLVM function, so it goes through the same backend lowering as the original. For CPU code this means the gradient runs on the same instruction selection and register allocation as the forward pass. For GPU code it means Enzyme can produce a backward kernel from a forward kernel without the user ever writing PTX or CUDA C.

The plugin is described in the README as performing AD of statically analyzable LLVM. The word statically analyzable is significant: Enzyme can differentiate code it can fully trace at compile time. Dynamic dispatch, calls into opaque external libraries, and runtime-variable control flow present problems not because Enzyme is unfinished but because the underlying LLVM IR does not carry enough information to synthesize a derivative in those cases. The project provides a custom rule mechanism specifically for those gaps, visible in examples/custom_rule.jl in the repository.

## Installing Enzyme.jl and Computing a First Gradient

Enzyme.jl installs through Julia's built-in package manager. At the Julia REPL, enter package mode with the ] key and run:

```julia
] add Enzyme
```

The package pulls in the Enzyme LLVM plugin as a compiled binary dependency through Julia's artifact system, so no separate LLVM build or C++ toolchain is required. After installation, the entry point is the autodiff function. The README gives this example:

```julia
using Enzyme, Test

f1(x) = x*x
@test first(autodiff(Reverse, f1, Active(1.0))[1]) ≈ 2.0
```

autodiff takes the differentiation mode as its first argument, then the function, then argument wrappers. Active(1.0) tells Enzyme to treat the argument as an active variable and accumulate its gradient. The return value is a tuple of active returns; first() extracts the gradient from the first element of that outer tuple. For f1(x) = x*x the derivative at x=1.0 is 2.0, confirming the calculation. The Reverse mode argument selects reverse-mode (backpropagation-style) differentiation, which is efficient for functions with many scalar inputs and a single scalar output.

The examples/ directory in the repository includes three files worth examining before writing production code. autodiff.jl covers the basic autodiff patterns. box.jl demonstrates a more complete scenario. custom_rule.jl shows how to supply hand-written derivative rules for functions Enzyme cannot differentiate automatically, a necessary skill for real codebases that call into specialized libraries.

## The Active Wrapper and Differentiation Modes

Enzyme's calling convention is more explicit than many Julia AD tools. Each argument to autodiff must be wrapped to declare how it participates in differentiation. Active marks a scalar argument as one whose gradient should be accumulated and returned. Arguments not wrapped as Active are treated as constants and do not contribute gradients. This explicitness is a design choice: it means Enzyme does not silently differentiate through arguments you did not intend to track, and it puts the structural decisions about which variables matter in front of the caller.

Reverse mode is the natural choice for loss functions and objective functions in optimization, where a single scalar output has gradients with respect to many parameters. The repository also supports forward mode, where the derivative propagates from input to output, which is more efficient for functions with few inputs and many outputs. The ext/ directory in the repository contains extensions for interoperability with other Julia packages, suggesting that the active/constant wrapping convention has been integrated with common array and differentiation interfaces beyond the standalone autodiff call.

The documentation lives at enzyme.mit.edu/julia and covers the full API, including batched derivatives, activity rules, and the treatment of mutation. The README points new users there rather than attempting to reproduce the full reference inline.

## GPU Differentiation and Performance Claims

One of the stated motivations for Enzyme.jl is GPU kernel differentiation. Julia GPU programming frameworks compile Julia code through LLVM to device IR, and Enzyme's plugin position in the LLVM pipeline means it can process those kernels at the same stage. A source-level AD tool would need to track every array operation through Julia's GPU array abstractions; Enzyme instead sees the compiled kernel and synthesizes a derivative kernel directly.

The README references two academic papers. The first, from NeurIPS 2020, introduced Enzyme and its LLVM-based approach. The second covers GPU differentiation alongside the performance optimization work. The papers provide the methodological foundation for the performance claims and are the required citations for academic users. Researchers who publish results using Enzyme.jl are asked to cite both, as noted in the README.

The benchmark/ directory in the repository contains performance comparisons. The README does not reproduce specific numbers inline, referring users to the published papers for quantitative results. The claim that Enzyme can meet or exceed state-of-the-art AD tools is a claim about the class of programs where LLVM-level differentiation has an advantage, not a universal performance guarantee.

## Limitations and When Enzyme.jl Will Fail

The README is direct: Enzyme.jl is a work in progress, and the project actively requests bug reports. Several patterns are known to cause problems or produce incorrect results. Code that calls into external C libraries through Julia's ccall mechanism is differentiable only if Enzyme can inspect and differentiate the underlying LLVM IR for that library, which is typically not possible for precompiled system libraries. For those cases, the user must supply a custom derivative rule. Programs that use try/catch error handling or certain forms of dynamic dispatch generate LLVM patterns that Enzyme cannot always trace statically, leading to compilation failures or wrong derivatives.

In-place mutation requires careful handling. Enzyme tracks the derivative of mutated values through the LLVM store and load instructions, but mutation patterns that alias memory in ways the compiler cannot statically resolve can produce incorrect gradients. This is an inherent challenge for any AD tool that must reason about memory at compile time.

The mailing list at groups.google.com/d/forum/enzyme-dev is the recommended channel for bug reports and questions. The project treats bug reports as contributions, and the CONTRIBUTING.md in the repository describes how to file them with enough information to reproduce the issue.

## Enzyme.jl vs Zygote.jl: LLVM vs Source-Level AD

Zygote.jl is the automatic differentiation library used by the Flux.jl deep learning ecosystem and is the most widely deployed Julia AD tool in machine learning workflows. Zygote operates at the Julia IR level, transforming the Julia compiler's intermediate representation before LLVM sees it. This source-level position gives Zygote good integration with Julia's type system and makes its error messages appear in Julia terms rather than LLVM terms, which aids debugging.

The trade-off is that Zygote cannot directly differentiate compiled GPU kernels or code that has already been lowered past Julia's IR. Enzyme.jl does not compete for that Flux.jl/Zygote.jl use case, because both tools in combination (Zygote for the model graph, Enzyme for kernels that Zygote cannot reach) are possible. Enzyme.jl becomes the better primary choice when the codebase consists of Julia GPU kernels, low-level array operations that produce poor gradients under Zygote, or mathematical code involving in-place operations that Zygote refuses to differentiate.

The comparison is not a quality ranking. Zygote.jl is a mature, production-tested library with a large ecosystem. Enzyme.jl is a research-grade tool with a different capability boundary. A team evaluating the two should start with Zygote for standard ML tasks and reach for Enzyme.jl when Zygote's boundary becomes the constraint.

## Maintenance Status and License

The last push to the Enzyme.jl repository was on 2026-09-28, and the most recent release is v0.13.205, published on 2026-09-24. The release cadence across September 2026 shows multiple patch updates per week, indicating active maintenance. The version number at v0.13.x reflects the pre-1.0 status that the README acknowledges with its work-in-progress language, but the frequency of releases suggests that production-blocking bugs are addressed quickly.

The project is licensed under the MIT license, which places no restrictions on commercial or academic use and requires only attribution. The homepage at enzymead.github.io/Enzyme.jl hosts the stable and development documentation builds. Users who need to stay on the latest stable API can follow the changelog linked from the documentation site; the dev branch documentation reflects unreleased changes.

The repository belongs to the EnzymeAD GitHub organization, which also hosts the core Enzyme LLVM plugin repository. Users who want to understand what Enzyme.jl can and cannot differentiate should read the core Enzyme documentation at enzyme.mit.edu, which explains the LLVM analysis passes and their limitations in more detail than the Julia-specific README.

## Conclusion

Enzyme.jl is the right choice when you need to differentiate Julia GPU kernels, tight numerical loops, or code that Zygote.jl cannot handle because it operates before LLVM sees the IR. Projects that run entirely inside Flux.jl and its Zygote.jl integration have less reason to switch unless a specific kernel proves intractable. Before adopting Enzyme.jl, test your target functions against the known rough edges: external C calls, error-handling control flow, and heavy dynamic dispatch all require custom rules. Review examples/custom_rule.jl to assess the implementation effort before committing to the migration.

## FAQ

### What is enzyme automatic differentiation and how does it work?

Enzyme is an LLVM compiler plugin that synthesizes derivative programs from LLVM intermediate representation. It runs after standard compiler optimization passes, so the derivative code inherits the same inlining, loop fusion, and register optimizations as the original. Enzyme.jl exposes this as a Julia package through the autodiff() function.

### How do I differentiate a Julia function in reverse mode with Enzyme.jl?

Call autodiff(Reverse, yourFunction, Active(inputValue)). The Active wrapper marks which argument contributes a gradient, and the return value is a tuple of active returns from which you extract the gradient with first().

### Can Enzyme.jl differentiate GPU kernels written in Julia?

Yes. Because Julia GPU code compiles through LLVM before being lowered to device IR, Enzyme can differentiate GPU kernels at the LLVM layer. The README and the accompanying research papers identify GPU kernel differentiation as one of Enzyme's primary advantages over source-level AD tools.

### What should I do when Enzyme.jl cannot differentiate a function automatically?

Supply a custom derivative rule for the parts it cannot trace. The repository includes examples/custom_rule.jl as a template. Functions that call external C libraries or rely on opaque runtime state require this approach.

## Sources

- [EnzymeAD/Enzyme.jl on GitHub](https://github.com/EnzymeAD/Enzyme.jl)
- [License: MIT](https://github.com/EnzymeAD/Enzyme.jl/blob/main/LICENSE)
- [Project website](https://enzymead.github.io/Enzyme.jl/)
- [README](https://github.com/EnzymeAD/Enzyme.jl/blob/main/README.md)
- [Releases](https://github.com/EnzymeAD/Enzyme.jl/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/enzymead-enzyme-jl
