EnzymeAD / Enzyme

High-performance automatic differentiation of LLVM and MLIR.
https://enzyme.mit.edu
Other
1.28k stars 108 forks source link

autodiff on structs with initial assignment after commit 5905cd6 #1213

Closed akdemironur closed 1 year ago

akdemironur commented 1 year ago

I'm getting an error after 5905cd6, here are my findings:

Consider the following code:

#include <stdio.h>
void __enzyme_autodiff(void *, ...);
int enzyme_const, enzyme_dup, enzyme_out, enzyme_dupnoneed;
typedef struct
{
    double u, v;
} Field;
void foo(Field *target, Field *Cells)
{
    target->u = 0;
    target->v = 0;

    target->u += Cells->u;
    target->v += Cells->v;
}

int main()
{
    Field *Ftarget;
    Field *grad_Ftarget;
    Field *cells;
    Field *grad_cells;
    __enzyme_autodiff(foo, enzyme_dupnoneed, &Ftarget, &grad_Ftarget, enzyme_dup, cells, grad_cells);
}

Compiling it after the commit 5905cd6 gives following output (I've written workarounds end of this issue):

[1/2] Building C object CMakeFiles/enzyme-test.dir/src/enzyme-test.c.o
FAILED: CMakeFiles/enzyme-test.dir/src/enzyme-test.c.o 
/home/ubuntu/enzyme-test/reqs/petsc/petsc_clang14_release/bin/mpicc  -I/home/ubuntu/enzyme-test/include -Wall -Wextra -Xclang -load -Xclang /home/ubuntu/enzyme-test/reqs/Enzyme/enzyme/build/Enzyme/ClangEnzyme-14.so -flegacy-pass-manager -ffast-math -Ofast -MD -MT CMakeFiles/enzyme-test.dir/src/enzyme-test.c.o -MF CMakeFiles/enzyme-test.dir/src/enzyme-test.c.o.d -o CMakeFiles/enzyme-test.dir/src/enzyme-test.c.o -c /home/ubuntu/enzyme-test/src/enzyme-test.c
/home/ubuntu/enzyme-test/src/enzyme-test.c:23:83: warning: variable 'cells' is uninitialized when used here [-Wuninitialized]
    __enzyme_autodiff(foo, enzyme_dupnoneed, &Ftarget, &grad_Ftarget, enzyme_dup, cells, grad_cells);
                                                                                  ^~~~~
/home/ubuntu/enzyme-test/src/enzyme-test.c:21:17: note: initialize the variable 'cells' to silence this warning
    Field *cells;
                ^
                 = NULL
/home/ubuntu/enzyme-test/src/enzyme-test.c:23:90: warning: variable 'grad_cells' is uninitialized when used here [-Wuninitialized]
    __enzyme_autodiff(foo, enzyme_dupnoneed, &Ftarget, &grad_Ftarget, enzyme_dup, cells, grad_cells);
                                                                                         ^~~~~~~~~~
/home/ubuntu/enzyme-test/src/enzyme-test.c:22:22: note: initialize the variable 'grad_cells' to silence this warning
    Field *grad_cells;
                     ^
                      = NULL
mod:; ModuleID = '/home/ubuntu/enzyme-test/src/enzyme-test.c'
source_filename = "/home/ubuntu/enzyme-test/src/enzyme-test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

%struct.Field = type { double, double }

@enzyme_dupnoneed = local_unnamed_addr global i32 0, align 4, !dbg !0
@enzyme_dup = local_unnamed_addr global i32 0, align 4, !dbg !9
@enzyme_const = local_unnamed_addr global i32 0, align 4, !dbg !5
@enzyme_out = local_unnamed_addr global i32 0, align 4, !dbg !11

; Function Attrs: mustprogress nofree norecurse nosync nounwind uwtable willreturn
define void @foo(%struct.Field* nocapture noundef writeonly %0, %struct.Field* nocapture noundef readonly %1) #0 !dbg !19 {
  call void @llvm.dbg.value(metadata %struct.Field* %0, metadata !30, metadata !DIExpression()), !dbg !32
  call void @llvm.dbg.value(metadata %struct.Field* %1, metadata !31, metadata !DIExpression()), !dbg !32
  %3 = getelementptr inbounds %struct.Field, %struct.Field* %0, i64 0, i32 0, !dbg !33
  %4 = getelementptr inbounds %struct.Field, %struct.Field* %0, i64 0, i32 1, !dbg !34
  %5 = getelementptr inbounds %struct.Field, %struct.Field* %1, i64 0, i32 0, !dbg !35
  %6 = bitcast %struct.Field* %0 to i8*, !dbg !35
  call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %6, i8 0, i64 16, i1 false), !dbg !36
  %7 = load double, double* %5, align 8, !dbg !35, !tbaa !37
  store double %7, double* %3, align 8, !dbg !42, !tbaa !37
  %8 = getelementptr inbounds %struct.Field, %struct.Field* %1, i64 0, i32 1, !dbg !43
  %9 = load double, double* %8, align 8, !dbg !43, !tbaa !44
  store double %9, double* %4, align 8, !dbg !45, !tbaa !44
  ret void, !dbg !46
}

; Function Attrs: nounwind uwtable
define i32 @main() local_unnamed_addr #1 !dbg !47 {
  %1 = alloca %struct.Field*, align 8
  %2 = alloca %struct.Field*, align 8
  %3 = bitcast %struct.Field** %1 to i8*, !dbg !55
  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %3) #6, !dbg !55
  %4 = bitcast %struct.Field** %2 to i8*, !dbg !56
  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %4) #6, !dbg !56
  %5 = load i32, i32* @enzyme_dupnoneed, align 4, !dbg !57, !tbaa !58
  %6 = load i32, i32* @enzyme_dup, align 4, !dbg !60, !tbaa !58
  call void @llvm.dbg.value(metadata %struct.Field** %1, metadata !51, metadata !DIExpression(DW_OP_deref)), !dbg !61
  call void @llvm.dbg.value(metadata %struct.Field** %2, metadata !52, metadata !DIExpression(DW_OP_deref)), !dbg !61
  %7 = bitcast %struct.Field** %1 to %struct.Field*, !dbg !62
  %8 = bitcast %struct.Field** %2 to %struct.Field*, !dbg !62
  call void (i8*, ...) @__enzyme_autodiff(i8* noundef bitcast (void (%struct.Field*, %struct.Field*)* @foo to i8*), i32 noundef %5, %struct.Field** noundef nonnull %1, %struct.Field** noundef nonnull %2, i32 noundef %6, %struct.Field* noundef undef, %struct.Field* noundef undef) #6, !dbg !62
  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %4) #6, !dbg !63
  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %3) #6, !dbg !63
  ret i32 0, !dbg !63
}

; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2

declare void @__enzyme_autodiff(i8* noundef, ...) local_unnamed_addr #3

; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2

; Function Attrs: mustprogress nofree nosync nounwind readnone speculatable willreturn
declare void @llvm.dbg.value(metadata, metadata, metadata) #4

; Function Attrs: argmemonly nofree nounwind willreturn writeonly
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #5

; Function Attrs: mustprogress nofree norecurse nosync nounwind uwtable willreturn
define void @preprocess_foo(%struct.Field* nocapture noundef writeonly %0, %struct.Field* nocapture noundef readonly %1) #0 !dbg !64 {
  call void @llvm.dbg.value(metadata %struct.Field* %0, metadata !66, metadata !DIExpression()) #7, !dbg !68
  call void @llvm.dbg.value(metadata %struct.Field* %1, metadata !67, metadata !DIExpression()) #7, !dbg !68
  %3 = getelementptr inbounds %struct.Field, %struct.Field* %0, i64 0, i32 0, !dbg !69
  %4 = getelementptr inbounds %struct.Field, %struct.Field* %0, i64 0, i32 1, !dbg !70
  %5 = getelementptr inbounds %struct.Field, %struct.Field* %1, i64 0, i32 0, !dbg !71
  %6 = bitcast %struct.Field* %0 to i8*, !dbg !71
  call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %6, i8 0, i64 16, i1 false) #7, !dbg !72
  %7 = load double, double* %5, align 8, !dbg !71, !tbaa !37
  store double %7, double* %3, align 8, !dbg !73, !tbaa !37
  %8 = getelementptr inbounds %struct.Field, %struct.Field* %1, i64 0, i32 1, !dbg !74
  %9 = load double, double* %8, align 8, !dbg !74, !tbaa !44
  store double %9, double* %4, align 8, !dbg !75, !tbaa !44
  ret void, !dbg !76
}

; Function Attrs: mustprogress nofree norecurse nosync nounwind uwtable willreturn
define internal void @diffefoo(%struct.Field* nocapture noundef writeonly %0, %struct.Field* nocapture %1, %struct.Field* nocapture noundef readonly %2, %struct.Field* nocapture %3) #0 !dbg !77 {
  %5 = getelementptr inbounds %struct.Field, %struct.Field* %1, i64 0, i32 0, !dbg !81
  %6 = phi double* , !dbg !81
  %7 = getelementptr inbounds %struct.Field, %struct.Field* %1, i64 0, i32 1, !dbg !82
  %8 = phi double* , !dbg !82
  %9 = getelementptr inbounds %struct.Field, %struct.Field* %3, i64 0, i32 0, !dbg !83
  %10 = phi double* , !dbg !83
  %11 = bitcast %struct.Field* %1 to i8*, !dbg !83
  %12 = phi i8* , !dbg !83
  call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %12, i8 0, i64 16, i1 false) #7, !dbg !84
  %13 = phi double , !dbg !83
  %14 = getelementptr inbounds %struct.Field, %struct.Field* %3, i64 0, i32 1, !dbg !85
  %15 = phi double* , !dbg !85
  %16 = phi double , !dbg !85
  br label %20, !dbg !86

17:                                               ; No predecessors!
  %18 = alloca double, align 8
  store double 0.000000e+00, double* %18, align 8
  %19 = alloca double, align 8
  store double 0.000000e+00, double* %19, align 8

20:                                               ; preds = %4
  %21 = load double, double* %7, align 8, !tbaa !44, !alias.scope !87, !noalias !90
  store double 0.000000e+00, double* %7, align 8, !dbg !92, !tbaa !44, !alias.scope !87, !noalias !90
  %22 = load double, double* %18, align 8
  %23 = fadd fast double %22, %21
  store double %23, double* %18, align 8
  %24 = load double, double* %18, align 8
  store double 0.000000e+00, double* %18, align 8
  %25 = load double, double* %14, align 8, !dbg !85, !tbaa !44, !alias.scope !93, !noalias !96
  %26 = fadd fast double %25, %24
  store double %26, double* %14, align 8, !dbg !85, !tbaa !44, !alias.scope !93, !noalias !96
  %27 = load double, double* %5, align 8, !tbaa !37, !alias.scope !87, !noalias !90
  store double 0.000000e+00, double* %5, align 8, !dbg !98, !tbaa !37, !alias.scope !87, !noalias !90
  %28 = load double, double* %19, align 8
  %29 = fadd fast double %28, %27
  store double %29, double* %19, align 8
  %30 = load double, double* %19, align 8
  store double 0.000000e+00, double* %19, align 8
  %31 = load double, double* %9, align 8, !dbg !83, !tbaa !37, !alias.scope !93, !noalias !96
  %32 = fadd fast double %31, %30
  store double %32, double* %9, align 8, !dbg !83, !tbaa !37, !alias.scope !93, !noalias !96
  call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %11, i8 0, i64 16, i1 false) #7, !dbg !84
  ret void
}

attributes #0 = { mustprogress nofree norecurse nosync nounwind uwtable willreturn "approx-func-fp-math"="true" "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" "frame-pointer"="none" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="true" }
attributes #1 = { nounwind uwtable "approx-func-fp-math"="true" "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" "frame-pointer"="none" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="true" }
attributes #2 = { argmemonly mustprogress nofree nosync nounwind willreturn }
attributes #3 = { "approx-func-fp-math"="true" "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" "frame-pointer"="none" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="true" }
attributes #4 = { mustprogress nofree nosync nounwind readnone speculatable willreturn }
attributes #5 = { argmemonly nofree nounwind willreturn writeonly }
attributes #6 = { nounwind }
attributes #7 = { mustprogress willreturn }

!llvm.dbg.cu = !{!2}
!llvm.module.flags = !{!13, !14, !15, !16, !17}
!llvm.ident = !{!18}

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "enzyme_dupnoneed", scope: !2, file: !7, line: 3, type: !8, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "Ubuntu clang version 14.0.6", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
!3 = !DIFile(filename: "/home/ubuntu/enzyme-test/src/enzyme-test.c", directory: "/home/ubuntu/enzyme-test/build", checksumkind: CSK_MD5, checksum: "01b485deb30d733dc6095fb74c6e6b49")
!4 = !{!5, !9, !11, !0}
!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
!6 = distinct !DIGlobalVariable(name: "enzyme_const", scope: !2, file: !7, line: 3, type: !8, isLocal: false, isDefinition: true)
!7 = !DIFile(filename: "src/enzyme-test.c", directory: "/home/ubuntu/enzyme-test", checksumkind: CSK_MD5, checksum: "01b485deb30d733dc6095fb74c6e6b49")
!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!9 = !DIGlobalVariableExpression(var: !10, expr: !DIExpression())
!10 = distinct !DIGlobalVariable(name: "enzyme_dup", scope: !2, file: !7, line: 3, type: !8, isLocal: false, isDefinition: true)
!11 = !DIGlobalVariableExpression(var: !12, expr: !DIExpression())
!12 = distinct !DIGlobalVariable(name: "enzyme_out", scope: !2, file: !7, line: 3, type: !8, isLocal: false, isDefinition: true)
!13 = !{i32 7, !"Dwarf Version", i32 5}
!14 = !{i32 2, !"Debug Info Version", i32 3}
!15 = !{i32 1, !"wchar_size", i32 4}
!16 = !{i32 7, !"PIC Level", i32 2}
!17 = !{i32 7, !"uwtable", i32 1}
!18 = !{!"Ubuntu clang version 14.0.6"}
!19 = distinct !DISubprogram(name: "foo", scope: !7, file: !7, line: 8, type: !20, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !29)
!20 = !DISubroutineType(types: !21)
!21 = !{null, !22, !22}
!22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64)
!23 = !DIDerivedType(tag: DW_TAG_typedef, name: "Field", file: !7, line: 7, baseType: !24)
!24 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !7, line: 4, size: 128, elements: !25)
!25 = !{!26, !28}
!26 = !DIDerivedType(tag: DW_TAG_member, name: "u", scope: !24, file: !7, line: 6, baseType: !27, size: 64)
!27 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float)
!28 = !DIDerivedType(tag: DW_TAG_member, name: "v", scope: !24, file: !7, line: 6, baseType: !27, size: 64, offset: 64)
!29 = !{!30, !31}
!30 = !DILocalVariable(name: "target", arg: 1, scope: !19, file: !7, line: 8, type: !22)
!31 = !DILocalVariable(name: "Cells", arg: 2, scope: !19, file: !7, line: 8, type: !22)
!32 = !DILocation(line: 0, scope: !19)
!33 = !DILocation(line: 10, column: 13, scope: !19)
!34 = !DILocation(line: 11, column: 13, scope: !19)
!35 = !DILocation(line: 13, column: 25, scope: !19)
!36 = !DILocation(line: 11, column: 15, scope: !19)
!37 = !{!38, !39, i64 0}
!38 = !{!"", !39, i64 0, !39, i64 8}
!39 = !{!"double", !40, i64 0}
!40 = !{!"omnipotent char", !41, i64 0}
!41 = !{!"Simple C/C++ TBAA"}
!42 = !DILocation(line: 13, column: 15, scope: !19)
!43 = !DILocation(line: 14, column: 25, scope: !19)
!44 = !{!38, !39, i64 8}
!45 = !DILocation(line: 14, column: 15, scope: !19)
!46 = !DILocation(line: 15, column: 1, scope: !19)
!47 = distinct !DISubprogram(name: "main", scope: !7, file: !7, line: 17, type: !48, scopeLine: 18, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !50)
!48 = !DISubroutineType(types: !49)
!49 = !{!8}
!50 = !{!51, !52, !53, !54}
!51 = !DILocalVariable(name: "Ftarget", scope: !47, file: !7, line: 19, type: !22)
!52 = !DILocalVariable(name: "grad_Ftarget", scope: !47, file: !7, line: 20, type: !22)
!53 = !DILocalVariable(name: "cells", scope: !47, file: !7, line: 21, type: !22)
!54 = !DILocalVariable(name: "grad_cells", scope: !47, file: !7, line: 22, type: !22)
!55 = !DILocation(line: 19, column: 5, scope: !47)
!56 = !DILocation(line: 20, column: 5, scope: !47)
!57 = !DILocation(line: 23, column: 28, scope: !47)
!58 = !{!59, !59, i64 0}
!59 = !{!"int", !40, i64 0}
!60 = !DILocation(line: 23, column: 71, scope: !47)
!61 = !DILocation(line: 0, scope: !47)
!62 = !DILocation(line: 23, column: 5, scope: !47)
!63 = !DILocation(line: 24, column: 1, scope: !47)
!64 = distinct !DISubprogram(name: "foo", scope: !7, file: !7, line: 8, type: !20, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !65)
!65 = !{!66, !67}
!66 = !DILocalVariable(name: "target", arg: 1, scope: !64, file: !7, line: 8, type: !22)
!67 = !DILocalVariable(name: "Cells", arg: 2, scope: !64, file: !7, line: 8, type: !22)
!68 = !DILocation(line: 0, scope: !64)
!69 = !DILocation(line: 10, column: 13, scope: !64)
!70 = !DILocation(line: 11, column: 13, scope: !64)
!71 = !DILocation(line: 13, column: 25, scope: !64)
!72 = !DILocation(line: 11, column: 15, scope: !64)
!73 = !DILocation(line: 13, column: 15, scope: !64)
!74 = !DILocation(line: 14, column: 25, scope: !64)
!75 = !DILocation(line: 14, column: 15, scope: !64)
!76 = !DILocation(line: 15, column: 1, scope: !64)
!77 = distinct !DISubprogram(name: "foo", scope: !7, file: !7, line: 8, type: !20, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !78)
!78 = !{!79, !80}
!79 = !DILocalVariable(name: "target", arg: 1, scope: !77, file: !7, line: 8, type: !22)
!80 = !DILocalVariable(name: "Cells", arg: 2, scope: !77, file: !7, line: 8, type: !22)
!81 = !DILocation(line: 10, column: 13, scope: !77)
!82 = !DILocation(line: 11, column: 13, scope: !77)
!83 = !DILocation(line: 13, column: 25, scope: !77)
!84 = !DILocation(line: 11, column: 15, scope: !77)
!85 = !DILocation(line: 14, column: 25, scope: !77)
!86 = !DILocation(line: 15, column: 1, scope: !77)
!87 = !{!88}
!88 = distinct !{!88, !89, !"shadow_0"}
!89 = distinct !{!89, !" diff: %"}
!90 = !{!91}
!91 = distinct !{!91, !89, !"primal"}
!92 = !DILocation(line: 14, column: 15, scope: !77)
!93 = !{!94}
!94 = distinct !{!94, !95, !"shadow_0"}
!95 = distinct !{!95, !" diff: %"}
!96 = !{!97}
!97 = distinct !{!97, !95, !"primal"}
!98 = !DILocation(line: 13, column: 15, scope: !77)

oldFunc:; Function Attrs: mustprogress nofree norecurse nosync nounwind uwtable willreturn
define void @preprocess_foo(%struct.Field* nocapture noundef writeonly %0, %struct.Field* nocapture noundef readonly %1) #0 !dbg !64 {
  call void @llvm.dbg.value(metadata %struct.Field* %0, metadata !66, metadata !DIExpression()) #6, !dbg !68
  call void @llvm.dbg.value(metadata %struct.Field* %1, metadata !67, metadata !DIExpression()) #6, !dbg !68
  %3 = getelementptr inbounds %struct.Field, %struct.Field* %0, i64 0, i32 0, !dbg !69
  %4 = getelementptr inbounds %struct.Field, %struct.Field* %0, i64 0, i32 1, !dbg !70
  %5 = getelementptr inbounds %struct.Field, %struct.Field* %1, i64 0, i32 0, !dbg !71
  %6 = bitcast %struct.Field* %0 to i8*, !dbg !71
  call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %6, i8 0, i64 16, i1 false) #6, !dbg !72
  %7 = load double, double* %5, align 8, !dbg !71, !tbaa !37
  store double %7, double* %3, align 8, !dbg !73, !tbaa !37
  %8 = getelementptr inbounds %struct.Field, %struct.Field* %1, i64 0, i32 1, !dbg !74
  %9 = load double, double* %8, align 8, !dbg !74, !tbaa !44
  store double %9, double* %4, align 8, !dbg !75, !tbaa !44
  ret void, !dbg !76
}

newFunc:; Function Attrs: mustprogress nofree norecurse nosync nounwind uwtable willreturn
define internal void @diffefoo(%struct.Field* nocapture noundef writeonly %0, %struct.Field* nocapture %1, %struct.Field* nocapture noundef readonly %2, %struct.Field* nocapture %3) #0 !dbg !77 {
  %5 = getelementptr inbounds %struct.Field, %struct.Field* %1, i64 0, i32 0, !dbg !81
  %6 = phi double* , !dbg !81
  %7 = getelementptr inbounds %struct.Field, %struct.Field* %1, i64 0, i32 1, !dbg !82
  %8 = phi double* , !dbg !82
  %9 = getelementptr inbounds %struct.Field, %struct.Field* %3, i64 0, i32 0, !dbg !83
  %10 = phi double* , !dbg !83
  %11 = bitcast %struct.Field* %1 to i8*, !dbg !83
  %12 = phi i8* , !dbg !83
  call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %12, i8 0, i64 16, i1 false) #6, !dbg !84
  %13 = phi double , !dbg !83
  %14 = getelementptr inbounds %struct.Field, %struct.Field* %3, i64 0, i32 1, !dbg !85
  %15 = phi double* , !dbg !85
  %16 = phi double , !dbg !85
  br label %20, !dbg !86

17:                                               ; No predecessors!
  %18 = alloca double, align 8
  store double 0.000000e+00, double* %18, align 8
  %19 = alloca double, align 8
  store double 0.000000e+00, double* %19, align 8

20:                                               ; preds = %4
  %21 = load double, double* %7, align 8, !tbaa !44, !alias.scope !87, !noalias !90
  store double 0.000000e+00, double* %7, align 8, !dbg !92, !tbaa !44, !alias.scope !87, !noalias !90
  %22 = load double, double* %18, align 8
  %23 = fadd fast double %22, %21
  store double %23, double* %18, align 8
  %24 = load double, double* %18, align 8
  store double 0.000000e+00, double* %18, align 8
  %25 = load double, double* %14, align 8, !dbg !85, !tbaa !44, !alias.scope !93, !noalias !96
  %26 = fadd fast double %25, %24
  store double %26, double* %14, align 8, !dbg !85, !tbaa !44, !alias.scope !93, !noalias !96
  %27 = load double, double* %5, align 8, !tbaa !37, !alias.scope !87, !noalias !90
  store double 0.000000e+00, double* %5, align 8, !dbg !98, !tbaa !37, !alias.scope !87, !noalias !90
  %28 = load double, double* %19, align 8
  %29 = fadd fast double %28, %27
  store double %29, double* %19, align 8
  %30 = load double, double* %19, align 8
  store double 0.000000e+00, double* %19, align 8
  %31 = load double, double* %9, align 8, !dbg !83, !tbaa !37, !alias.scope !93, !noalias !96
  %32 = fadd fast double %31, %30
  store double %32, double* %9, align 8, !dbg !83, !tbaa !37, !alias.scope !93, !noalias !96
  call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %11, i8 0, i64 16, i1 false) #6, !dbg !84
  ret void
}

 pp:   %12 = phi i8* , !dbg !34 of   %6 = bitcast %struct.Field* %0 to i8*, !dbg !35
clang-14: /home/ubuntu/enzyme-test/reqs/Enzyme/enzyme/Enzyme/GradientUtils.cpp:7932: void GradientUtils::eraseFictiousPHIs(): Assertion `pp->getNumUses() == 0' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.  Program arguments: clang-14 -fPIC -Qunused-arguments -g -O3 -Wno-implicit-function-declaration -I/home/ubuntu/enzyme-test/include -Wall -Wextra -Xclang -load -Xclang /home/ubuntu/enzyme-test/reqs/Enzyme/enzyme/build/Enzyme/ClangEnzyme-14.so -flegacy-pass-manager -ffast-math -Ofast -MD -MT CMakeFiles/enzyme-test.dir/src/enzyme-test.c.o -MF CMakeFiles/enzyme-test.dir/src/enzyme-test.c.o.d -o CMakeFiles/enzyme-test.dir/src/enzyme-test.c.o -c /home/ubuntu/enzyme-test/src/enzyme-test.c -I/home/ubuntu/enzyme-test/reqs/petsc/petsc_clang14_release/include
1.  <eof> parser at end of file
2.  Per-module optimization passes
3.  Running pass 'Enzyme Pass' on module '/home/ubuntu/enzyme-test/src/enzyme-test.c'.
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
/lib/x86_64-linux-gnu/libLLVM-14.so.1(_ZN4llvm3sys15PrintStackTraceERNS_11raw_ostreamEi+0x31)[0x7f4b7924e6c1]
/lib/x86_64-linux-gnu/libLLVM-14.so.1(_ZN4llvm3sys17RunSignalHandlersEv+0xee)[0x7f4b7924c3fe]
/lib/x86_64-linux-gnu/libLLVM-14.so.1(_ZN4llvm3sys15CleanupOnSignalEm+0xfb)[0x7f4b7924da6b]
/lib/x86_64-linux-gnu/libLLVM-14.so.1(+0xd7a74f)[0x7f4b7917a74f]
/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7f4b77c42520]
/lib/x86_64-linux-gnu/libc.so.6(pthread_kill+0x12c)[0x7f4b77c96a7c]
/lib/x86_64-linux-gnu/libc.so.6(raise+0x16)[0x7f4b77c42476]
/lib/x86_64-linux-gnu/libc.so.6(abort+0xd3)[0x7f4b77c287f3]
/lib/x86_64-linux-gnu/libc.so.6(+0x2871b)[0x7f4b77c2871b]
/lib/x86_64-linux-gnu/libc.so.6(+0x39e96)[0x7f4b77c39e96]
/home/ubuntu/enzyme-test/reqs/Enzyme/enzyme/build/Enzyme/ClangEnzyme-14.so(_ZN13GradientUtils17eraseFictiousPHIsEv+0x40b)[0x7f4b756ec339]
/home/ubuntu/enzyme-test/reqs/Enzyme/enzyme/build/Enzyme/ClangEnzyme-14.so(_ZN11EnzymeLogic23CreatePrimalAndGradientEOK15ReverseCacheKeyR12TypeAnalysisPK15AugmentedReturnb+0x4c22)[0x7f4b7548da90]
/home/ubuntu/enzyme-test/reqs/Enzyme/enzyme/build/Enzyme/ClangEnzyme-14.so(+0x80e398)[0x7f4b7540e398]
/home/ubuntu/enzyme-test/reqs/Enzyme/enzyme/build/Enzyme/ClangEnzyme-14.so(+0x810336)[0x7f4b75410336]
/home/ubuntu/enzyme-test/reqs/Enzyme/enzyme/build/Enzyme/ClangEnzyme-14.so(+0x81451b)[0x7f4b7541451b]
/home/ubuntu/enzyme-test/reqs/Enzyme/enzyme/build/Enzyme/ClangEnzyme-14.so(+0x81568e)[0x7f4b7541568e]
/home/ubuntu/enzyme-test/reqs/Enzyme/enzyme/build/Enzyme/ClangEnzyme-14.so(+0x816b35)[0x7f4b75416b35]
/lib/x86_64-linux-gnu/libLLVM-14.so.1(_ZN4llvm6legacy15PassManagerImpl3runERNS_6ModuleE+0x946)[0x7f4b79389926]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZN5clang17EmitBackendOutputERNS_17DiagnosticsEngineERKNS_19HeaderSearchOptionsERKNS_14CodeGenOptionsERKNS_13TargetOptionsERKNS_11LangOptionsEN4llvm9StringRefEPNSE_6ModuleENS_13BackendActionESt10unique_ptrINSE_17raw_pwrite_streamESt14default_deleteISK_EE+0x359b)[0x7f4b8065b2cb]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(+0x1b7dfa5)[0x7f4b8097dfa5]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZN5clang17MultiplexConsumer21HandleTranslationUnitERNS_10ASTContextE+0x2c)[0x7f4b8135203c]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZN5clang8ParseASTERNS_4SemaEbb+0x244)[0x7f4b7f805454]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZN5clang13CodeGenAction13ExecuteActionEv+0xb1)[0x7f4b8097a2c1]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZN5clang14FrontendAction7ExecuteEv+0x67)[0x7f4b81316e07]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZN5clang16CompilerInstance13ExecuteActionERNS_14FrontendActionE+0x336)[0x7f4b8126e656]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZN5clang25ExecuteCompilerInvocationEPNS_16CompilerInstanceE+0x29b)[0x7f4b8139071b]
clang-14(_Z8cc1_mainN4llvm8ArrayRefIPKcEES2_Pv+0x99b)[0x41328b]
clang-14[0x4114bc]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(+0x20ec872)[0x7f4b80eec872]
/lib/x86_64-linux-gnu/libLLVM-14.so.1(_ZN4llvm20CrashRecoveryContext9RunSafelyENS_12function_refIFvvEEE+0xdd)[0x7f4b7917a4bd]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZNK5clang6driver10CC1Command7ExecuteEN4llvm8ArrayRefINS2_8OptionalINS2_9StringRefEEEEEPNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEPb+0x140)[0x7f4b80eec360]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZNK5clang6driver11Compilation14ExecuteCommandERKNS0_7CommandERPS3_+0x3f3)[0x7f4b80eb6b63]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZNK5clang6driver11Compilation11ExecuteJobsERKNS0_7JobListERN4llvm15SmallVectorImplISt4pairIiPKNS0_7CommandEEEE+0x8a)[0x7f4b80eb6dea]
/lib/x86_64-linux-gnu/libclang-cpp.so.14(_ZN5clang6driver6Driver18ExecuteCompilationERNS0_11CompilationERN4llvm15SmallVectorImplISt4pairIiPKNS0_7CommandEEEE+0x1a7)[0x7f4b80ed0ea7]
clang-14(main+0x2816)[0x410f26]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f4b77c29d90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f4b77c29e40]
clang-14(_start+0x25)[0x40e3b5]
clang: error: clang frontend command failed with exit code 134 (use -v to see invocation)
Ubuntu clang version 14.0.6
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
clang: note: diagnostic msg: 
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang: note: diagnostic msg: /tmp/enzyme-test-bd24bb.c
clang: note: diagnostic msg: /tmp/enzyme-test-bd24bb.sh
clang: note: diagnostic msg: 

********************
ninja: build stopped: subcommand failed.

Any release before that commit gives following output as expected(of course with warnings):

[1/2] Building C object CMakeFiles/enzyme-test.dir/src/enzyme-test.c.o
/home/ubuntu/enzyme-test/src/enzyme-test.c:23:83: warning: variable 'cells' is uninitialized when used here [-Wuninitialized]
    __enzyme_autodiff(foo, enzyme_dupnoneed, &Ftarget, &grad_Ftarget, enzyme_dup, cells, grad_cells);
                                                                                  ^~~~~
/home/ubuntu/enzyme-test/src/enzyme-test.c:21:17: note: initialize the variable 'cells' to silence this warning
    Field *cells;
                ^
                 = NULL
/home/ubuntu/enzyme-test/src/enzyme-test.c:23:90: warning: variable 'grad_cells' is uninitialized when used here [-Wuninitialized]
    __enzyme_autodiff(foo, enzyme_dupnoneed, &Ftarget, &grad_Ftarget, enzyme_dup, cells, grad_cells);
                                                                                         ^~~~~~~~~~
/home/ubuntu/enzyme-test/src/enzyme-test.c:22:22: note: initialize the variable 'grad_cells' to silence this warning
    Field *grad_cells;
                     ^
                      = NULL
2 warnings generated.
[2/2] Linking C executable enzyme-test

An easy fix I'm using is basically deleting initial assignment, following code compiles fine with newer releases too:

#include <stdio.h>
void __enzyme_autodiff(void *, ...);
int enzyme_const, enzyme_dup, enzyme_out, enzyme_dupnoneed;
typedef struct
{
    double u, v;
} Field;
void foo(Field *target, Field *Cells)
{
    // target->u = 0;
    // target->v = 0;

    target->u = Cells->u;
    target->v = Cells->v;
}

int main()
{
    Field *Ftarget;
    Field *grad_Ftarget;
    Field *cells;
    Field *grad_cells;
    __enzyme_autodiff(foo, enzyme_dupnoneed, &Ftarget, &grad_Ftarget, enzyme_dup, cells, grad_cells);
}

Another user fix is don't using enzyme_dupnoneed:

#include <stdio.h>
void __enzyme_autodiff(void *, ...);
int enzyme_const, enzyme_dup, enzyme_out, enzyme_dupnoneed;
typedef struct
{
    double u, v;
} Field;
void foo(Field *target, Field *Cells)
{
    target->u = 0;
    target->v = 0;

    target->u += Cells->u;
    target->v += Cells->v;
}

int main()
{
    Field *Ftarget;
    Field *grad_Ftarget;
    Field *cells;
    Field *grad_cells;
    __enzyme_autodiff(foo, enzyme_dup, &Ftarget, &grad_Ftarget, enzyme_dup, cells, grad_cells);
}

(Also, as I see this problem only occurs with structs with more than 1 variable)

wsmoses commented 1 year ago
define void @foo(double* nocapture %0) {
  %2 = bitcast double* %0 to i8*
  tail call void @llvm.memset.p0i8.i64(i8* nonnull align 8 dereferenceable(16) %2, i8 0, i64 16, i1 false)
  ret void
}

; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1)

; Function Attrs: nounwind uwtable
define void @caller(double* %0, double* %1) {
  tail call void (...) @__enzyme_autodiff(void (double*)* @foo, metadata !"enzyme_dupnoneed", double* %0, double* %1)
  ret void
}

declare void @__enzyme_autodiff(...) 
wsmoses commented 1 year ago

Fixed by https://github.com/EnzymeAD/Enzyme/pull/1214