Skip to content

Forward-over-reverse runtime indexed getfield hits idx_jl_getfield_aug calling convention mismatch #3296

Description

@ChrisRackauckas-Claude

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:

grad([0.5]) = [1.0]

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:

Enzyme v0.13.176

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions