EnzymeAD / EnzymeAD/Enzyme.jl

Julia 1.12 regression: `EnzymeNoDerivativeError: cannot handle unknown binary operator: shl` in forward-over-reverse HVP

Open
#3,368 5 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.