Julia 1.12 regression: `EnzymeNoDerivativeError: cannot handle unknown binary operator: shl` in forward-over-reverse HVP
- Dominant language
- Julia
- Stars
- 586
- Forks
- 108
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 44
Description
Hi all, DI's Enzyme backend has been failing CI on 1.12 for a while (https://github.com/JuliaDiff/DifferentiationInterface.jl/issues/855) and I let Fable have a go. It claims to have found a regression between `v0.13.185` and `v0.13.188`.
Unfortunately, the issue can't be replicated on macOS and appears to be a Linux bug. I'm pasting Fable's analysis below. CC @gdalle
___
DifferentiationInterface.jl's CI job `1.12 - DI Back (Enzyme)` (ubuntu-latest, x64, Julia 1.12) started failing between Enzyme v0.13.181 and v0.13.185 and is still failing on v0.13.188. All errors are the same `EnzymeNoDerivativeError` raised while forward-mode-differentiating a reverse-mode gradient (forward-over-reverse HVP/Hessian) of a broadcast-`mapreduce` function, with runtime activity enabled on both modes:
```
EnzymeNoDerivativeError: Current scope:
define internal fastcc void @preprocess_diffejulia_arr_to_num_linalg_1006664(...)
...
cannot handle unknown binary operator: %5724 = shl i64 %value_phi200324.unr617_unwrap, 3
```
The `shl i64 %..., 3` with the `.unr*_unwrap` suffix is integer index arithmetic (×8 bytes) in an unrolled-loop epilogue, which the AD core fails to treat as inactive.
## Regression window
| Date (2026) | Enzyme | Enzyme_jll | `1.12 - DI Back (Enzyme)` on DI `main` |
|---|---|---|---|
| Jul 7–11 | v0.13.181 | 0.0.281 | ✅ pass ([run](https://github.com/JuliaDiff/DifferentiationInterface.jl/actions/runs/29151887625)) |
| Jul 15–16 | v0.13.185 | 0.0.282+0 | ❌ 12 errors ([job](https://github.com/JuliaDiff/DifferentiationInterface.jl/actions/runs/29422368615/job/87381771792)) |
| Jul 19 | v0.13.188 | 0.0.285+0 | ❌ same errors ([job](https://github.com/JuliaDiff/DifferentiationInterface.jl/actions/runs/29689805205/job/88200405543)) |
DI `main` did not change in that window (the failures predate any DI commit in it), so the regression came in with Enzyme v0.13.182–v0.13.185 — that window includes the Enzyme_jll 0.0.282 bump (#3326) plus #3325, #3331, #3335, #3336, #3340, #3341.
## Failing function and call structure
```julia
arr_to_num_linalg(x::AbstractArray) = sum(vec(x .^ 4) .* transpose(vec(x .^ 6)))
f_cache(x, c) = (c[1] = arr_to_num_linalg(x); c[1]) # result stored in a Duplicated cache
```
differentiated as HVP = forward-over-reverse with runtime activity on both modes: `Enzyme.autodiff(Forward, shuffled_gradient, ...)` where `shuffled_gradient` internally calls `Enzyme.autodiff(Reverse, ...)`. Failing inputs: `float.(1:6)` and `float.(reshape(1:6, 2, 3))`; both out-of-place and in-place HVP/Hessian variants fail (12 errors total). Condensed stacktrace from the Jul 19 job:
```
[1] arr_to_num_linalg @ ./essentials.jl:0
[2] StoreInCache @ DifferentiationInterfaceTest/src/scenarios/modify.jl:225
[3] diffejulia_StoreInCache_1006654wrap
[5] enzyme_call @ Enzyme/src/compiler.jl:6471
[6] CombinedAdjointThunk @ Enzyme/src/compiler.jl:6355
[7-8] autodiff (Reverse) @ Enzyme/src/Enzyme.jl:539,560
[9-11] gradient / shuffled_gradient @ DifferentiationInterface (Enzyme ext)
[12] fwddiffejulia_shuffled_gradient_1006732wrap
[14] enzyme_call @ Enzyme/src/compiler.jl:6471
[15] ForwardModeThunk @ Enzyme/src/compiler.jl:6371
[16-18] autodiff (Forward) @ Enzyme/src/Enzyme.jl:691,580,552
[19-21] value_and_pushforward / gradient_and_hvp @ DifferentiationInterface
```
## Reproducibility (important caveat)
We could **not** reproduce this outside DI's Linux x64 CI environment, despite an extensive attempt matrix on macOS (Julia 1.12.6, exact CI package versions Enzyme v0.13.185 + jll 0.0.282):
- arm64 native and x64 via Rosetta
- with/without CI flags (`-O1 --code-coverage=user`)
- the exact DIT test-harness subset (same backend type parameters, same cachified scenarios): 532/532 pass
The failure appears to require linux x86_64 codegen and/or the compilation state accumulated over DI's full ~90-min test suite (compiled-function caching/merging across the session). CI logs with the full IR dump are in the linked jobs above.
DI-level reproducer (fails only in the CI environment described above):
```julia
using DifferentiationInterface # qualify hvp — Enzyme also exports one
using Enzyme
arr_to_num_linalg(x::AbstractArray) = sum(vec(x .^ 4) .* transpose(vec(x .^ 6)))
f_cache(x, c) = (c[1] = arr_to_num_linalg(x); c[1])
backend = SecondOrder(
AutoEnzyme(; mode = Enzyme.set_runtime_activity(Enzyme.Forward)),
AutoEnzyme(; mode = Enzyme.set_runtime_activity(Enzyme.Reverse)),
)
x = float.(1:6) # also fails with float.(reshape(1:6, 2, 3)) in CI
DifferentiationInterface.hvp(f_cache, backend, x, (float.(-1:-1:-6),), Cache([0.0]))
```
This is the same test family previously affected by #2854 (closed Jun 26), but that fix held through `v0.13.181`. This is a new, distinct regression.
Contributor guide
Assessment
This issue has not been assessed yet.