`try`/`catch` block error on Julia 1.8 and earlier
This works on Julia 1.9 and Enzyme main (https://github.com/EnzymeAD/Enzyme.jl/commit/6692fad483dbe4002be9d254dae959c634d841c9):
using Enzyme
function f(x)
try
sqrt(-1.0)
return 3x
catch
return 2x
end
end
autodiff(Reverse, f, Active, Active(2.0))[1][1]
2.0
But on Julia 1.8.5 (and earlier?) it errors. I'm struggling to catch the segfault but it is probably the same one as at https://github.com/EnzymeAD/Enzyme.jl/actions/runs/5707501454/job/15464242433?pr=969:
signal (11): Segmentation fault
in expression starting at /home/runner/work/Enzyme.jl/Enzyme.jl/test/runtests.jl:258
unknown function (ip: 0x7ff1ebe36081)
autodiff at /home/runner/work/Enzyme.jl/Enzyme.jl/src/Enzyme.jl:222
unknown function (ip: 0x7ff1ebfd7282)
_jl_invoke at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/gf.c:2377 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/gf.c:2559
jl_apply at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/julia.h:1843 [inlined]
do_call at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:126
eval_value at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:215
eval_stmt_value at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:166 [inlined]
eval_body at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:612
eval_body at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:522
eval_body at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:522
eval_body at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:522
jl_interpret_toplevel_thunk at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:750
jl_toplevel_eval_flex at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/toplevel.c:906
jl_toplevel_eval_flex at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/toplevel.c:850
ijl_toplevel_eval_in at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/toplevel.c:965
eval at ./boot.jl:368 [inlined]
include_string at ./loading.jl:1428
_jl_invoke at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/gf.c:2377 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/gf.c:2559
_include at ./loading.jl:1488
include at ./client.jl:476
unknown function (ip: 0x7ff20faef5f2)
_jl_invoke at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/gf.c:2377 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/gf.c:2559
jl_apply at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/julia.h:1843 [inlined]
do_call at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:126
eval_value at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:215
eval_stmt_value at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:166 [inlined]
eval_body at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:612
jl_interpret_toplevel_thunk at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/interpreter.c:750
jl_toplevel_eval_flex at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/toplevel.c:906
jl_toplevel_eval_flex at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/toplevel.c:850
ijl_toplevel_eval_in at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/toplevel.c:965
eval at ./boot.jl:368 [inlined]
exec_options at ./client.jl:276
_start at ./client.jl:522
jfptr__start_38041.clone_1 at /opt/hostedtoolcache/julia/1.8.5/x64/lib/julia/sys.so (unknown line)
_jl_invoke at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/gf.c:2377 [inlined]
ijl_apply_generic at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/gf.c:2559
jl_apply at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/julia.h:1843 [inlined]
true_main at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/jlapi.c:575
jl_repl_entrypoint at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/src/jlapi.c:719
main at /cache/build/default-amdci4-2/julialang/julia-release-1-dot-8/cli/loader_exe.c:59
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
unknown function (ip: 0x401098)
Allocations: 344129554 (Pool: 343937740; Big: 191814); GC: 258
ERROR: LoadError: Package Enzyme errored during testing (received signal: 11)
Stacktrace:
[1] pkgerror(msg::String)
@ Pkg.Types /opt/hostedtoolcache/julia/1.8.5/x64/share/julia/stdlib/v1.8/Pkg/src/Types.jl:67
[2] test(ctx::Pkg.Types.Context, pkgs::Vector{Pkg.Types.PackageSpec}; coverage::Bool, julia_args::Cmd, test_args::Cmd, test_fn::Nothing, force_latest_compatible_version::Bool, allow_earlier_backwards_compatible_versions::Bool, allow_reresolve::Bool)
@ Pkg.Operations /opt/hostedtoolcache/julia/1.8.5/x64/share/julia/stdlib/v1.8/Pkg/src/Operations.jl:1813
[3] test(ctx::Pkg.Types.Context, pkgs::Vector{Pkg.Types.PackageSpec}; coverage::Bool, test_fn::Nothing, julia_args::Vector{String}, test_args::Cmd, force_latest_compatible_version::Bool, allow_earlier_backwards_compatible_versions::Bool, allow_reresolve::Bool, kwargs::Base.Pairs{Symbol, IOContext{Base.PipeEndpoint}, Tuple{Symbol}, NamedTuple{(:io,), Tuple{IOContext{Base.PipeEndpoint}}}})
@ Pkg.API /opt/hostedtoolcache/julia/1.8.5/x64/share/julia/stdlib/v1.8/Pkg/src/API.jl:434
[4] test(pkgs::Vector{Pkg.Types.PackageSpec}; io::IOContext{Base.PipeEndpoint}, kwargs::Base.Pairs{Symbol, Any, Tuple{Symbol, Symbol, Symbol}, NamedTuple{(:coverage, :julia_args, :force_latest_compatible_version), Tuple{Bool, Vector{String}, Bool}}})
@ Pkg.API /opt/hostedtoolcache/julia/1.8.5/x64/share/julia/stdlib/v1.8/Pkg/src/API.jl:156
[5] test(; name::Nothing, uuid::Nothing, version::Nothing, url::Nothing, rev::Nothing, path::Nothing, mode::Pkg.Types.PackageMode, subdir::Nothing, kwargs::Base.Pairs{Symbol, Any, Tuple{Symbol, Symbol, Symbol}, NamedTuple{(:coverage, :julia_args, :force_latest_compatible_version), Tuple{Bool, Vector{String}, Bool}}})
@ Pkg.API /opt/hostedtoolcache/julia/1.8.5/x64/share/julia/stdlib/v1.8/Pkg/src/API.jl:171
[6] top-level scope
@ ~/work/_actions/julia-actions/julia-runtest/v1/test_harness.jl:15
[7] include(fname::String)
@ Base.MainInclude ./client.jl:476
[8] top-level scope
@ none:1
in expression starting at /home/runner/work/_actions/julia-actions/julia-runtest/v1/test_harness.jl:7
Looks like what's happening here is that we prove that sqrt result is not needed for the derivative and it is dead code eliminated. We should run a postprocessing to emit a new "unreachable reached" error on such unreachables, so that the same error is throw condition is applied.
That error message will be somethign along the lines of "the original primal code is guaranteed to hit this error condition, thus differentiating it does not make sense"
We should probably handle exceptional Control-Flow better, gradients could propagate through PhiC nodes.
This fixes the issue for Julia 1.7 and 1.8 but on Julia 1.6.7 it returns the wrong answer:
using Enzyme
function f(x)
try
sqrt(-1.0)
return 3x
catch
return 2x
end
end
autodiff(Reverse, f, Active, Active(2.0))[1][1]
0.0
julia> autodiff(Reverse, f, Active, Active(2.0))[1][1]
after simplification :
; Function Attrs: willreturn
define double @preprocess_julia_f_4904_inner.1(double %0) local_unnamed_addr #16 !dbg !102 {
entry:
%phic.i = alloca double, align 8
%phic.i.0.sroa_cast2 = bitcast double* %phic.i to i8*
call void @llvm.lifetime.start.p0i8(i64 8, i8* %phic.i.0.sroa_cast2)
%1 = call {}*** @julia.ptls_states() #17
%2 = call i64 @jl_excstack_state() #18, !dbg !103
%3 = call i32 @julia.except_enter() #19, !dbg !103
%4 = icmp eq i32 %3, 0, !dbg !103
br i1 %4, label %try.i, label %julia_f_4904_inner.exit, !dbg !103
try.i: ; preds = %entry
store volatile double %0, double* %phic.i, align 8, !dbg !103, !tbaa !70, !noalias !105
%5 = call fastcc nonnull {} addrspace(10)* @julia_throw_complex_domainerror_4907() #20, !dbg !108
unreachable, !dbg !108
julia_f_4904_inner.exit: ; preds = %entry
%phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0. = load volatile double, double* %phic.i, align 8, !dbg !110
call void @jl_pop_handler(i32 1) #21, !dbg !110
%6 = fmul double %phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0., 2.000000e+00, !dbg !111
call void @jl_restore_excstack(i64 %2) #21, !dbg !114
%phic.i.0.sroa_cast3 = bitcast double* %phic.i to i8*, !dbg !114
call void @llvm.lifetime.end.p0i8(i64 8, i8* %phic.i.0.sroa_cast3), !dbg !114
ret double %6, !dbg !115
}
; Function Attrs: willreturn
define internal { double } @diffejulia_f_4904_inner.1(double %0, double %differeturn) local_unnamed_addr #16 !dbg !116 {
entry:
%"'de" = alloca double, align 8
%1 = getelementptr double, double* %"'de", i64 0
store double 0.000000e+00, double* %1, align 8
%phic.i = alloca double, align 8
%2 = call {}*** @julia.ptls_states() #17
%3 = call i64 @jl_excstack_state() #18, !dbg !117
%4 = call i32 @julia.except_enter() #19, !dbg !117
%5 = icmp eq i32 %4, 0, !dbg !117
br i1 %5, label %try.i, label %julia_f_4904_inner.exit, !dbg !117
try.i: ; preds = %entry
store volatile double %0, double* %phic.i, align 8, !dbg !117, !tbaa !70, !noalias !119
%6 = call fastcc nonnull {} addrspace(10)* @julia_throw_complex_domainerror_4907() #20, !dbg !122
unreachable
julia_f_4904_inner.exit: ; preds = %entry
%phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0.phic.i.0. = load volatile double, double* %phic.i, align 8, !dbg !124, !alias.scope !125, !noalias !128
call void @jl_pop_handler(i32 1) #21, !dbg !124
call void @jl_restore_excstack(i64 %3) #21, !dbg !130
br label %invertjulia_f_4904_inner.exit, !dbg !131
invertentry: ; preds = %invertjulia_f_4904_inner.exit
%7 = load double, double* %"'de", align 8
%8 = insertvalue { double } undef, double %7, 0
ret { double } %8
invertjulia_f_4904_inner.exit: ; preds = %julia_f_4904_inner.exit
br label %invertentry
}
0.0
Correction to the above, the wrong answer is also returned on Julia 1.7.3.