EnzymeAD / EnzymeAD/Enzyme

Fortran: No forward mode derivative found for _FortranAAssign

Open
#3,008 3 comments 0 reactions 0 assignees View on GitHub
fortran
Dominant language
LLVM
Stars
1.7k
Forks
188
Avg merge
1d 22h
Merged PRs (30d)
26

Description

When copying the value of an array into another array, enzyme cannot propagate the derivatives properly. See example below.

```fortran
PROGRAM main
USE enzyme, only: enzyme_const, enzyme_dup, enzyme_autodiff, enzyme_out, enzyme_fwddiff

IMPLICIT NONE (TYPE, EXTERNAL)

INTEGER, PARAMETER :: n = 3

DOUBLE PRECISION, DIMENSION(n) :: a, da, y, dy
a = 1.0D0
da = 0.0D0
y = 0.0D0
dy = 1.0D0

WRITE(*, *) "Before computation:"
WRITE(*, *) " a:", a, "memory address", LOC(a)
WRITE(*, *) " y:", y, "memory address", LOC(y)
CALL computation(n, a, y)
WRITE(*, *) "After computation:"
WRITE(*, *) " a:", a, "memory address", LOC(a)
WRITE(*, *) " y:", y, "memory address", LOC(y)
WRITE(*, *) "Memory addresses unchanged, but values adjusted"

CALL enzyme_fwddiff(computation, enzyme_const, n, enzyme_dup, a, da, &
enzyme_dup, y, dy)

CONTAINS

subroutine computation(n, a, y)
implicit none
INTEGER, INTENT(IN) :: n
DOUBLE PRECISION, INTENT(IN), DIMENSION(n) :: a
DOUBLE PRECISION, INTENT(OUT), DIMENSION(n) :: y

y = a * 2.0D0
end subroutine computation

END PROGRAM main

```
This results in the following output during the enzyme pass
```
flang-22 -fno-fast-math -fno-unroll-loops -fno-vectorize -pedantic -flto -c -O0 enzyme_function_hooks.f90 -o enzyme_function_hooks.ll
flang-22 -fno-fast-math -fno-unroll-loops -fno-vectorize -pedantic -flto -c -O0 enzyme.f90 -o enzyme.ll
flang-22 -fno-fast-math -fno-unroll-loops -fno-vectorize -pedantic -flto -c -O0 enzyme_copy.f90 -o enzyme_copy.ll
llvm-link-22 enzyme_function_hooks.ll enzyme.ll enzyme_copy.ll -o combined.ll
opt-22 combined.ll -load-pass-plugin=/home/tobiasdijkhuis/Enzyme/enzyme/build/Enzyme/LLVMEnzyme-22.so -load=/home/tobiasdijkhuis/Enzyme/enzyme/build/Enzyme/LLVMEnzyme-22.so -passes="enzyme" -enzyme-print -o ad.ll
prefn:
define internal void @_QFPcomputation(ptr noalias readonly captures(none) %0, ptr noalias readonly captures(none) %1, ptr noalias %2) #0 {
%4 = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8
%5 = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8
%6 = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, i64 1, align 8
%7 = load i32, ptr %0, align 4
%8 = sext i32 %7 to i64
%9 = icmp sgt i64 %8, 0
%10 = select i1 %9, i64 %8, i64 0
%11 = mul i64 8, %10
%12 = icmp sgt i64 %11, 0
%13 = select i1 %12, i64 %11, i64 1
%14 = call ptr @malloc(i64 %13)
%15 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } { ptr undef, i64 8, i32 20240719, i8 1, i8 28, i8 0, i8 0, [1 x [3 x i64]] [[3 x i64] [i64 1, i64 undef, i64 undef]] }, i64 %10, 7, 0, 1
%16 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %15, i64 8, 7, 0, 2
%17 = mul i64 8, %10
%18 = mul i64 1, %10
%19 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %16, ptr %14, 0
store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %19, ptr %5, align 8
br label %20

20: ; preds = %24, %3
%21 = phi i64 [ %39, %24 ], [ 1, %3 ]
%22 = phi i64 [ %40, %24 ], [ %10, %3 ]
%23 = icmp sgt i64 %22, 0
br i1 %23, label %24, label %41

24: ; preds = %20
%25 = sub nsw i64 %21, 1
%26 = mul nsw i64 %25, 1
%27 = mul nsw i64 %26, 1
%28 = add nsw i64 %27, 0
%29 = mul nsw i64 1, %10
%30 = getelementptr double, ptr %1, i64 %28
%31 = load double, ptr %30, align 8
%32 = fmul contract double %31, 2.000000e+00
%33 = sub nsw i64 %21, 1
%34 = mul nsw i64 %33, 1
%35 = mul nsw i64 %34, 1
%36 = add nsw i64 %35, 0
%37 = mul nsw i64 1, %10
%38 = getelementptr double, ptr %14, i64 %36
store double %32, ptr %38, align 8
%39 = add nsw i64 %21, 1
%40 = sub i64 %22, 1
br label %20

41: ; preds = %20
%42 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } { ptr undef, i64 8, i32 20240719, i8 1, i8 28, i8 0, i8 0, [1 x [3 x i64]] [[3 x i64] [i64 1, i64 undef, i64 undef]] }, i64 %10, 7, 0, 1
%43 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %42, i64 8, 7, 0, 2
%44 = mul i64 8, %10
%45 = mul i64 1, %10
%46 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %43, ptr %2, 0
store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %46, ptr %4, align 8
call void @llvm.memcpy.p0.p0.i32(ptr %6, ptr %4, i32 48, i1 false)
call void @_FortranAAssign(ptr %6, ptr %5, ptr @_QQclX0aa5ac8c74550ca4c15ebe4ab1f7dec6, i32 34)
call void @free(ptr %14)
ret void
}

after simplification :
; Function Attrs: mustprogress willreturn
define internal void @preprocess__QFPcomputation(ptr noalias readonly captures(none) %0, ptr noalias readonly captures(none) %1, ptr noalias %2) #3 {
%4 = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8
%5 = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, i64 1, align 8
%6 = load i32, ptr %0, align 4
%7 = sext i32 %6 to i64
%8 = icmp sgt i64 %7, 0
%9 = select i1 %8, i64 %7, i64 0
%10 = mul i64 8, %9
%11 = icmp sgt i64 %10, 0
%12 = select i1 %11, i64 %10, i64 1
%13 = call ptr @malloc(i64 %12) #4
%14 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } { ptr undef, i64 8, i32 20240719, i8 1, i8 28, i8 0, i8 0, [1 x [3 x i64]] [[3 x i64] [i64 1, i64 undef, i64 undef]] }, i64 %9, 7, 0, 1
%15 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %14, i64 8, 7, 0, 2
%16 = mul i64 8, %9
%17 = mul i64 1, %9
%18 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %15, ptr %13, 0
%.fca.0.extract = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %18, 0
%.fca.0.gep = getelementptr inbounds { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %4, i32 0, i32 0
store ptr %.fca.0.extract, ptr %.fca.0.gep, align 8
%.fca.1.extract = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %18, 1
%.fca.1.gep = getelementptr inbounds { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %4, i32 0, i32 1
store i64 %.fca.1.extract, ptr %.fca.1.gep, align 8
%.fca.2.extract = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %18, 2
%.fca.2.gep = getelementptr inbounds { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %4, i32 0, i32 2
store i32 %.fca.2.extract, ptr %.fca.2.gep, align 8
%.fca.3.extract = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %18, 3
%.fca.3.gep = getelementptr inbounds { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %4, i32 0, i32 3
store i8 %.fca.3.extract, ptr %.fca.3.gep, align 4
%.fca.4.extract = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %18, 4
%.fca.4.gep = getelementptr inbounds { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %4, i32 0, i32 4
store i8 %.fca.4.extract, ptr %.fca.4.gep, align 1
%.fca.5.extract = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %18, 5
%.fca.5.gep = getelementptr inbounds { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %4, i32 0, i32 5
store i8 %.fca.5.extract, ptr %.fca.5.gep, align 2
%.fca.6.extract = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %18, 6
%.fca.6.gep = getelementptr inbounds { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %4, i32 0, i32 6
store i8 %.fca.6.extract, ptr %.fca.6.gep, align 1
%.fca.7.0.0.extract = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %18, 7, 0, 0
%.fca.7.0.0.gep = getelementptr inbounds { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %4, i32 0, i32 7, i32 0, i32 0
store i64 %.fca.7.0.0.extract, ptr %.fca.7.0.0.gep, align 8
%.fca.7.0.1.extract = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %18, 7, 0, 1
%.fca.7.0.1.gep = getelementptr inbounds { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %4, i32 0, i32 7, i32 0, i32 1
store i64 %.fca.7.0.1.extract, ptr %.fca.7.0.1.gep, align 8
%.fca.7.0.2.extract = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %18, 7, 0, 2
%.fca.7.0.2.gep = getelementptr inbounds { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %4, i32 0, i32 7, i32 0, i32 2
store i64 %.fca.7.0.2.extract, ptr %.fca.7.0.2.gep, align 8
br label %19

19: ; preds = %23, %3
%iv = phi i64 [ %iv.next, %23 ], [ 0, %3 ]
%20 = mul nsw i64 %iv, -1
%iv.next = add nuw nsw i64 %iv, 1
%21 = add nsw i64 %9, %20
%22 = icmp sgt i64 %21, 0
br i1 %22, label %23, label %40

23: ; preds = %19
%24 = sub nsw i64 %iv.next, 1
%25 = mul nsw i64 %24, 1
%26 = mul nsw i64 %25, 1
%27 = add nsw i64 %26, 0
%28 = mul nsw i64 1, %9
%29 = getelementptr double, ptr %1, i64 %27
%30 = load double, ptr %29, align 8
%31 = fmul contract double %30, 2.000000e+00
%32 = sub nsw i64 %iv.next, 1
%33 = mul nsw i64 %32, 1
%34 = mul nsw i64 %33, 1
%35 = add nsw i64 %34, 0
%36 = mul nsw i64 1, %9
%37 = getelementptr double, ptr %13, i64 %35
store double %31, ptr %37, align 8
%38 = add nsw i64 %iv.next, 1
%39 = sub i64 %21, 1
br label %19

40: ; preds = %19
%41 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } { ptr undef, i64 8, i32 20240719, i8 1, i8 28, i8 0, i8 0, [1 x [3 x i64]] [[3 x i64] [i64 1, i64 undef, i64 undef]] }, i64 %9, 7, 0, 1
%42 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %41, i64 8, 7, 0, 2
%43 = mul i64 8, %9
%44 = mul i64 1, %9
%45 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %42, ptr %2, 0
%.fca.0.extract1 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 0
%.fca.1.extract3 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 1
%.fca.2.extract5 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 2
%.fca.3.extract7 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 3
%.fca.4.extract9 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 4
%.fca.5.extract11 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 5
%.fca.6.extract13 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 6
%.fca.7.0.0.extract15 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 7, 0, 0
%.fca.7.0.1.extract17 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 7, 0, 1
%.fca.7.0.2.extract19 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 7, 0, 2
store ptr %.fca.0.extract1, ptr %5, align 1
%.sroa.2.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 8
store i64 %.fca.1.extract3, ptr %.sroa.2.0..sroa_idx, align 1
%.sroa.3.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 16
store i32 %.fca.2.extract5, ptr %.sroa.3.0..sroa_idx, align 1
%.sroa.4.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 20
store i8 %.fca.3.extract7, ptr %.sroa.4.0..sroa_idx, align 1
%.sroa.5.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 21
store i8 %.fca.4.extract9, ptr %.sroa.5.0..sroa_idx, align 1
%.sroa.6.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 22
store i8 %.fca.5.extract11, ptr %.sroa.6.0..sroa_idx, align 1
%.sroa.7.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 23
store i8 %.fca.6.extract13, ptr %.sroa.7.0..sroa_idx, align 1
%.sroa.8.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 24
store i64 %.fca.7.0.0.extract15, ptr %.sroa.8.0..sroa_idx, align 1
%.sroa.9.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 32
store i64 %.fca.7.0.1.extract17, ptr %.sroa.9.0..sroa_idx, align 1
%.sroa.10.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 40
store i64 %.fca.7.0.2.extract19, ptr %.sroa.10.0..sroa_idx, align 1
call void @_FortranAAssign(ptr %5, ptr %4, ptr @_QQclX0aa5ac8c74550ca4c15ebe4ab1f7dec6, i32 34) #4
call void @free(ptr %13) #4
ret void
}

error: :0:0: in function preprocess__QFPcomputation void (ptr, ptr, ptr): Enzyme: No forward mode derivative found for _FortranAAssign
at context: call void @_FortranAAssign(ptr %5, ptr %4, ptr @_QQclX0aa5ac8c74550ca4c15ebe4ab1f7dec6, i32 34) #4

make: *** [Makefile:74: enzyme_copy] Error 1
19: ; preds = %23, %3
%iv = phi i64 [ %iv.next, %23 ], [ 0, %3 ]
%20 = mul nsw i64 %iv, -1
%iv.next = add nuw nsw i64 %iv, 1
%21 = add nsw i64 %9, %20
%22 = icmp sgt i64 %21, 0
br i1 %22, label %23, label %40

23: ; preds = %19
%24 = sub nsw i64 %iv.next, 1
%25 = mul nsw i64 %24, 1
%26 = mul nsw i64 %25, 1
%27 = add nsw i64 %26, 0
%28 = mul nsw i64 1, %9
%29 = getelementptr double, ptr %1, i64 %27
%30 = load double, ptr %29, align 8
%31 = fmul contract double %30, 2.000000e+00
%32 = sub nsw i64 %iv.next, 1
%33 = mul nsw i64 %32, 1
%34 = mul nsw i64 %33, 1
%35 = add nsw i64 %34, 0
%36 = mul nsw i64 1, %9
%37 = getelementptr double, ptr %13, i64 %35
store double %31, ptr %37, align 8
%38 = add nsw i64 %iv.next, 1
%39 = sub i64 %21, 1
br label %19

40: ; preds = %19
%41 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } { ptr undef, i64 8, i32 20240719, i8 1, i8 28, i8 0, i8 0, [1 x [3 x i64]] [[3 x i64] [i64 1, i64 undef, i64 undef]] }, i64 %9, 7, 0, 1
%42 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %41, i64 8, 7, 0, 2
%43 = mul i64 8, %9
%44 = mul i64 1, %9
%45 = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %42, ptr %2, 0
%.fca.0.extract1 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 0
%.fca.1.extract3 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 1
%.fca.2.extract5 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 2
%.fca.3.extract7 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 3
%.fca.4.extract9 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 4
%.fca.5.extract11 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 5
%.fca.6.extract13 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 6
%.fca.7.0.0.extract15 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 7, 0, 0
%.fca.7.0.1.extract17 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 7, 0, 1
%.fca.7.0.2.extract19 = extractvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %45, 7, 0, 2
store ptr %.fca.0.extract1, ptr %5, align 1
%.sroa.2.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 8
store i64 %.fca.1.extract3, ptr %.sroa.2.0..sroa_idx, align 1
%.sroa.3.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 16
store i32 %.fca.2.extract5, ptr %.sroa.3.0..sroa_idx, align 1
%.sroa.4.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 20
store i8 %.fca.3.extract7, ptr %.sroa.4.0..sroa_idx, align 1
%.sroa.5.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 21
store i8 %.fca.4.extract9, ptr %.sroa.5.0..sroa_idx, align 1
%.sroa.6.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 22
store i8 %.fca.5.extract11, ptr %.sroa.6.0..sroa_idx, align 1
%.sroa.7.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 23
store i8 %.fca.6.extract13, ptr %.sroa.7.0..sroa_idx, align 1
%.sroa.8.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 24
store i64 %.fca.7.0.0.extract15, ptr %.sroa.8.0..sroa_idx, align 1
%.sroa.9.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 32
store i64 %.fca.7.0.1.extract17, ptr %.sroa.9.0..sroa_idx, align 1
%.sroa.10.0..sroa_idx = getelementptr inbounds i8, ptr %5, i64 40
store i64 %.fca.7.0.2.extract19, ptr %.sroa.10.0..sroa_idx, align 1
call void @_FortranAAssign(ptr %5, ptr %4, ptr @_QQclX0aa5ac8c74550ca4c15ebe4ab1f7dec6, i32 29) #4
call void @free(ptr %13) #4
ret void
}

error: :0:0: in function preprocess__QFPcomputation void (ptr, ptr, ptr): Enzyme: No forward mode derivative found for _FortranAAssign
at context: call void @_FortranAAssign(ptr %5, ptr %4, ptr @_QQclX0aa5ac8c74550ca4c15ebe4ab1f7dec6, i32 29) #4

make: *** [Makefile:74: enzyme_copy] Error 1
```
The analogous result is found when using `enzyme_autodiff` instead.

Contributor guide

Open the contributing guide

Research direction

Reproduce the failure using the shown Fortran program and the flang, llvm-link, and opt commands. Start by tracing the generated _FortranAAssign call in enzyme_function_hooks.f90, enzyme.f90, enzyme_copy.f90, and the resulting combined.ll or ad.ll. Done means forward-mode differentiation succeeds for the array assignment without the reported missing derivative.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.