Summary
Forward-over-reverse differentiation fails on a small Enzyme-only MWE involving a runtime-indexed getfield of an immutable struct field that is a Vector{Float64}. The inner reverse pass succeeds; the outer forward-over-reverse pass fails while compiling idx_jl_getfield_aug.
This is the isolated reduction from #3280, filed separately because the original issue was SciML/OrdinaryDiffEq-heavy and closed.
MWE
import Enzyme
const RA_R = Enzyme.set_runtime_activity(Enzyme.Reverse)
const RA_F = Enzyme.set_runtime_activity(Enzyme.Forward)
struct TwoFields
a::Float64
p::Vector{Float64}
end
@noinline function indexed_runtime_getfield(x, i)
getfield(Base.inferencebarrier(x), Base.inferencebarrier(i))
end
@noinline function loss(p)
q = indexed_runtime_getfield(TwoFields(1.0, p), 2)
sum(abs2, q)
end
@noinline function grad(p)
dp = zero(p)
Enzyme.autodiff(RA_R, Enzyme.Const(loss), Enzyme.Active, Enzyme.Duplicated(p, dp))
dp
end
@show grad([0.5])
@show Enzyme.autodiff(RA_F, Enzyme.Const(p -> sum(grad(p))), Enzyme.Duplicated([0.5], [1.0]))
Observed
The inner reverse succeeds:
Then the outer forward-over-reverse call fails:
ERROR: LoadError: CallingConventionMismatchError: Enzyme hit an internal error trying to parse the julia calling convention definition for:
idx_jl_getfield_aug(::Val{@NamedTuple{1}}, ::Base.RefValue{TwoFields}, ::Type{Val{1}}, ::Val{false})
@ Enzyme.Compiler .../src/rules/typeunstablerules.jl:1272
expectLen != length(parameters(f))
{} addrspace(10)* ({} addrspace(10)*, {} addrspace(10)**, i32, {} addrspace(10)**)
expectLen=1
has_swiftself=false
has_sret=false
has_returnroots=false
source_sig=Tuple{typeof(Enzyme.Compiler.idx_jl_getfield_aug), Val{@NamedTuple{1}}, Base.RefValue{TwoFields}, Type{Val{1}}, Val{false}}
parmsRemoved=UInt64[]
Versions tested
I ran this locally on Julia 1.11.9 against the current registered/latest Enzyme release:
I also fetched origin/main; at the time of testing, origin/main was the same commit as the v0.13.176 tag:
e619385288e167ba236034e5d4881de1a6264168
Command used for the registered-release check:
JULIA_DEPOT_PATH=/home/crackauc/sandbox/tmp_20260706_131032_27625/.julia_depot_release \
/home/crackauc/.juliaup/bin/julia +1.11 --project=repro_release repro/issue3280_reduced.jl
Notes
A one-field version does not reproduce for me:
struct HasVector
p::Vector{Float64}
end
with getfield(..., 1) returns (2.0,) for the outer forward run. A two-field concrete struct with runtime-indexed getfield(..., 2) is enough to trigger the failure.
The original SciML/OrdinaryDiffEq reproducer from #3280 also still fails with latest registered packages (DiffEqBase v7.6.0, OrdinaryDiffEq v7.1.1, SciMLSensitivity v7.113.0, Enzyme v0.13.176), but this issue is meant to track the dependency-free Enzyme-only failure.
Summary
Forward-over-reverse differentiation fails on a small Enzyme-only MWE involving a runtime-indexed
getfieldof an immutable struct field that is aVector{Float64}. The inner reverse pass succeeds; the outer forward-over-reverse pass fails while compilingidx_jl_getfield_aug.This is the isolated reduction from #3280, filed separately because the original issue was SciML/OrdinaryDiffEq-heavy and closed.
MWE
Observed
The inner reverse succeeds:
Then the outer forward-over-reverse call fails:
Versions tested
I ran this locally on Julia 1.11.9 against the current registered/latest Enzyme release:
I also fetched
origin/main; at the time of testing,origin/mainwas the same commit as thev0.13.176tag:Command used for the registered-release check:
Notes
A one-field version does not reproduce for me:
with
getfield(..., 1)returns(2.0,)for the outer forward run. A two-field concrete struct with runtime-indexedgetfield(..., 2)is enough to trigger the failure.The original SciML/OrdinaryDiffEq reproducer from #3280 also still fails with latest registered packages (
DiffEqBase v7.6.0,OrdinaryDiffEq v7.1.1,SciMLSensitivity v7.113.0,Enzyme v0.13.176), but this issue is meant to track the dependency-free Enzyme-only failure.