Fortran: No forward mode derivative found for _FortranAAssign
- 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
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