; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --include-generated-funcs
; RUN: if [ %llvmver -lt 16 ]; then %opt < %s %loadEnzyme -enzyme -enzyme-preopt=false -mem2reg -instsimplify -adce -correlated-propagation -simplifycfg -adce -S | FileCheck %s; fi
; RUN: %opt < %s %newLoadEnzyme -passes="enzyme,function(mem2reg,instsimplify,adce,correlated-propagation,%simplifycfg,adce)" -enzyme-preopt=false -S | FileCheck %s

source_filename = "/mnt/pci4/wmdata/Enzyme2/enzyme/test/Integration/ReverseMode/eigensumsqdyn.cpp"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

$_ZNK5Eigen9EigenBaseINS_6MatrixIdLin1ELin1ELi0ELin1ELin1EEEE4colsEv = comdat any

define void @caller(i8* %a, i8* %b1, i8* %b2, i8* %b3, i8* %c) local_unnamed_addr {
entry:
  call void (i8*, ...) @__enzyme_fwddiff(i8* bitcast (void (double**, i64*)* @_ZL6matvecPKN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEES3_ to i8*), metadata !"enzyme_width", i64 3 , i8* %a, i8* %b1, i8* %b2, i8* %b3, i8* %c)
  ret void
}

declare void @__enzyme_fwddiff(i8*, ...) local_unnamed_addr

; Function Attrs: noinline nounwind uwtable
define internal void @_ZL6matvecPKN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEES3_(double** noalias %m_data.i.i.i, i64* noalias %m_rows) #0 {
entry:
  call void @subcall(double** nonnull %m_data.i.i.i, i64* nonnull %m_rows) #3
  store i64 0, i64* %m_rows, align 8
  ret void
}

; Function Attrs: inlinehint norecurse nounwind uwtable
define linkonce_odr dso_local i64 @_ZNK5Eigen9EigenBaseINS_6MatrixIdLin1ELin1ELi0ELin1ELin1EEEE4colsEv(i64* %m_cols) local_unnamed_addr #1 comdat align 2 {
entry:
  %tmp.i.i = load i64, i64* %m_cols, align 8, !tbaa !2
  ret i64 %tmp.i.i
}

; Function Attrs: nounwind uwtable
define void @subcall(double** %m_data.i.i.i, i64* %i7) local_unnamed_addr #2 {
entry:
  %mat = load double*, double** %m_data.i.i.i, align 8, !tbaa !8
  %cols = call i64 @_ZNK5Eigen9EigenBaseINS_6MatrixIdLin1ELin1ELi0ELin1ELin1EEEE4colsEv(i64* %i7) #3
  br label %for.body

for.body:                                         ; preds = %for.body, %entry
  %i = phi i64 [ %inc, %for.body ], [ 0, %entry ]
  %call = getelementptr inbounds double, double* %mat, i64 %i
  %ld = load double, double* %call, align 8
  %fmul = fmul double %ld, %ld
  store double %fmul, double* %call, align 8, !tbaa !11
  %inc = add nuw nsw i64 %i, 1
  %exitcond = icmp eq i64 %inc, %cols
  br i1 %exitcond, label %exit, label %for.body

exit:                                             ; preds = %for.body
  ret void
}

attributes #0 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { inlinehint norecurse nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #3 = { nounwind }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 7.1.0 "}
!2 = !{!3, !7, i64 16}
!3 = !{!"_ZTSN5Eigen12DenseStorageIdLin1ELin1ELin1ELi0EEE", !4, i64 0, !7, i64 8, !7, i64 16}
!4 = !{!"any pointer", !5, i64 0}
!5 = !{!"omnipotent char", !6, i64 0}
!6 = !{!"Simple C++ TBAA"}
!7 = !{!"long", !5, i64 0}
!8 = !{!9, !4, i64 0}
!9 = !{!"_ZTSN5Eigen8internal9evaluatorINS_15PlainObjectBaseINS_6MatrixIdLin1ELin1ELi0ELin1ELin1EEEEEEE", !4, i64 0, !10, i64 8}
!10 = !{!"_ZTSN5Eigen8internal19variable_if_dynamicIlLin1EEE", !7, i64 0}
!11 = !{!12, !12, i64 0}
!12 = !{!"double", !5, i64 0}


; CHECK: define {{[^@]+}}@fwddiffe3_ZL6matvecPKN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEES3_(double** noalias [[M_DATA_I_I_I:%.*]], [3 x double**] %"m_data.i.i.i'", i64* noalias [[M_ROWS:%.*]]) 
; CHECK-NEXT:  entry:
; CHECK-NEXT:    call void @fwddiffe3subcall(double** [[M_DATA_I_I_I]], [3 x double**] %"m_data.i.i.i'", i64* [[M_ROWS]])
; CHECK-NEXT:    store i64 0, i64* [[M_ROWS]], align 8
; CHECK-NEXT:    ret void
;
;
; CHECK: define {{[^@]+}}@fwddiffe3subcall(double** [[M_DATA_I_I_I:%.*]], [3 x double**] %"m_data.i.i.i'", i64* [[I7:%.*]])
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP0:%.*]] = extractvalue [3 x double**] %"m_data.i.i.i'", 0
; CHECK-NEXT:    %"mat'ipl" = load double*, double** [[TMP0]], align 8, !tbaa !8
; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue [3 x double**] %"m_data.i.i.i'", 1
; CHECK-NEXT:    %"mat'ipl1" = load double*, double** [[TMP1]], align 8, !tbaa !8
; CHECK-NEXT:    [[TMP2:%.*]] = extractvalue [3 x double**] %"m_data.i.i.i'", 2
; CHECK-NEXT:    %"mat'ipl2" = load double*, double** [[TMP2]], align 8, !tbaa !8
; CHECK-NEXT:    [[MAT:%.*]] = load double*, double** [[M_DATA_I_I_I]], align 8, !tbaa !8
; CHECK-NEXT:    [[COLS:%.*]] = call i64 @_ZNK5Eigen9EigenBaseINS_6MatrixIdLin1ELin1ELi0ELin1ELin1EEEE4colsEv(i64* [[I7]]) [[ATTR6:#.*]]
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    %"call'ipg" = getelementptr inbounds double, double* %"mat'ipl", i64 [[IV]]
; CHECK-NEXT:    %"call'ipg3" = getelementptr inbounds double, double* %"mat'ipl1", i64 [[IV]]
; CHECK-NEXT:    %"call'ipg4" = getelementptr inbounds double, double* %"mat'ipl2", i64 [[IV]]
; CHECK-NEXT:    [[CALL:%.*]] = getelementptr inbounds double, double* [[MAT]], i64 [[IV]]
; CHECK-NEXT:    %"ld'ipl" = load double, double* %"call'ipg", align 8
; CHECK-NEXT:    %"ld'ipl5" = load double, double* %"call'ipg3", align 8
; CHECK-NEXT:    %"ld'ipl6" = load double, double* %"call'ipg4", align 8
; CHECK-NEXT:    [[LD:%.*]] = load double, double* [[CALL]], align 8
; CHECK-NEXT:    [[FMUL:%.*]] = fmul double [[LD]], [[LD]]
; CHECK-NEXT:    [[TMP3:%.*]] = fmul fast double %"ld'ipl", [[LD]]
; CHECK-NEXT:    [[TMP6:%.*]] = fmul fast double %"ld'ipl5", [[LD]]
; CHECK-NEXT:    [[TMP9:%.*]] = fmul fast double %"ld'ipl6", [[LD]]
; CHECK-NEXT:    [[TMP4:%.*]] = fmul fast double %"ld'ipl", [[LD]]
; CHECK-NEXT:    [[TMP7:%.*]] = fmul fast double %"ld'ipl5", [[LD]]
; CHECK-NEXT:    [[TMP10:%.*]] = fmul fast double %"ld'ipl6", [[LD]]
; CHECK-NEXT:    [[TMP5:%.*]] = fadd fast double [[TMP3]], [[TMP4]]
; CHECK-NEXT:    [[TMP8:%.*]] = fadd fast double [[TMP6]], [[TMP7]]
; CHECK-NEXT:    [[TMP11:%.*]] = fadd fast double [[TMP9]], [[TMP10]]
; CHECK-NEXT:    store double [[TMP5]], double* %"call'ipg", align 8
; CHECK-NEXT:    store double [[TMP8]], double* %"call'ipg3", align 8
; CHECK-NEXT:    store double [[TMP11]], double* %"call'ipg4", align 8
; CHECK-NEXT:    store double [[FMUL]], double* [[CALL]], align 8, !tbaa !11
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i64 [[IV_NEXT]], [[COLS]]
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[EXIT:%.*]], label [[FOR_BODY]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
