; 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 -gvn -instsimplify -adce -correlated-propagation -simplifycfg -S | FileCheck %s; fi
; RUN: %opt < %s %newLoadEnzyme -passes="enzyme,function(mem2reg,gvn,instsimplify,adce,correlated-propagation,%simplifycfg)" -enzyme-preopt=false -S | FileCheck %s

%struct.Gradients = type { double, double, double }


; Function Attrs: nounwind
declare %struct.Gradients @__enzyme_fwddiff(i8*, ...)

; Function Attrs: noinline norecurse nounwind uwtable
define  double @f(double* nocapture %x, i64 %n) #0 {
entry:
  br label %loop

loop:
  %j = phi i64 [ %nj, %end ], [ 0, %entry ]
  %sum = phi double [ %nsum, %end ], [ 0.000000e+00, %entry ]
  %nj = add nsw nuw i64 %j, 1
  %g0 = getelementptr inbounds double, double* %x, i64 %j
  br label %body

body:                              ; preds = %entry, %for.cond.cleanup6
  %i = phi i64 [ %next, %body ], [ 0, %loop ]
  %idx = phi i64 [ %nidx, %body ], [ 0, %loop ]
  %gep = getelementptr inbounds double, double* %g0, i64 %i
  %ld = load double, double* %gep, align 8
  %cmp = fcmp oeq double %ld, 3.141592e+00
  %next = add nuw i64 %i, 1
  %int = fptoui double %ld to i64
  %nidx = add nuw i64 %idx, %int
  br i1 %cmp, label %body, label %end

end:
  %gep2 = getelementptr inbounds double, double* %x, i64 %idx
  %ld2 = load double, double* %gep2, align 8
  %nsum = fadd double %ld2, %sum
  %cmp2 = icmp ne i64 %nj, 10
  br i1 %cmp2, label %loop, label %exit

exit:
  ret double %nsum
}

; Function Attrs: noinline nounwind uwtable
define dso_local %struct.Gradients @dsumsquare(double* %x, double* %xp1, double* %xp2, double* %xp3, i64 %n) local_unnamed_addr #1 {
entry:
  %call = tail call %struct.Gradients (i8*, ...) @__enzyme_fwddiff(i8* bitcast (double (double*, i64)* @f to i8*), metadata !"enzyme_width", i64 3, double* %x, double* %xp1, double* %xp2, double* %xp3, i64 %n)
  ret %struct.Gradients %call
}

attributes #0 = { noinline norecurse nounwind uwtable }
attributes #1 = { noinline nounwind uwtable }

; CHECK: define {{[^@]+}}@fwddiffe3f(double* nocapture [[X:%.*]], [3 x double*] %"x'", i64 [[N:%.*]]) 
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[TMP0_0:%.*]] = phi {{(fast )?}}double [ [[TMP5:%.*]], [[END:%.*]] ], [ 0.000000e+00, [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[TMP0_1:%.*]] = phi {{(fast )?}}double [ [[TMP8:%.*]], [[END:%.*]] ], [ 0.000000e+00, [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[TMP0_2:%.*]] = phi {{(fast )?}}double [ [[TMP11:%.*]], [[END:%.*]] ], [ 0.000000e+00, [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[END]] ], [ 0, [[ENTRY]] ]
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[G0:%.*]] = getelementptr inbounds double, double* [[X]], i64 [[IV]]
; CHECK-NEXT:    br label [[BODY:%.*]]
; CHECK:       body:
; CHECK-NEXT:    [[IV1:%.*]] = phi i64 [ [[IV_NEXT2:%.*]], [[BODY]] ], [ 0, [[LOOP]] ]
; CHECK-NEXT:    [[IDX:%.*]] = phi i64 [ [[NIDX:%.*]], [[BODY]] ], [ 0, [[LOOP]] ]
; CHECK-NEXT:    [[IV_NEXT2]] = add nuw nsw i64 [[IV1]], 1
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds double, double* [[G0]], i64 [[IV1]]
; CHECK-NEXT:    [[LD:%.*]] = load double, double* [[GEP]], align 8
; CHECK-NEXT:    [[CMP:%.*]] = fcmp oeq double [[LD]], 0x400921FAFC8B007A
; CHECK-NEXT:    [[INT:%.*]] = fptoui double [[LD]] to i64
; CHECK-NEXT:    [[NIDX]] = add nuw i64 [[IDX]], [[INT]]
; CHECK-NEXT:    br i1 [[CMP]], label [[BODY]], label [[END]]
; CHECK:       end:
; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue [3 x double*] %"x'", 0
; CHECK-NEXT:    %"gep2'ipg" = getelementptr inbounds double, double* [[TMP1]], i64 [[IDX]]
; CHECK-NEXT:    [[TMP2:%.*]] = extractvalue [3 x double*] %"x'", 1
; CHECK-NEXT:    %"gep2'ipg1" = getelementptr inbounds double, double* [[TMP2]], i64 [[IDX]]
; CHECK-NEXT:    [[TMP3:%.*]] = extractvalue [3 x double*] %"x'", 2
; CHECK-NEXT:    %"gep2'ipg2" = getelementptr inbounds double, double* [[TMP3]], i64 [[IDX]]
; CHECK-NEXT:    %"ld2'ipl" = load double, double* %"gep2'ipg", align 8
; CHECK-NEXT:    %"ld2'ipl3" = load double, double* %"gep2'ipg1", align 8
; CHECK-NEXT:    %"ld2'ipl4" = load double, double* %"gep2'ipg2", align 8
; CHECK-NEXT:    [[TMP5]] = fadd fast double %"ld2'ipl", [[TMP0_0]]
; CHECK-NEXT:    [[TMP6:%.*]] = insertvalue [3 x double] undef, double [[TMP5]], 0
; CHECK-NEXT:    [[TMP8]] = fadd fast double %"ld2'ipl3", [[TMP0_1]]
; CHECK-NEXT:    [[TMP9:%.*]] = insertvalue [3 x double] [[TMP6]], double [[TMP8]], 1
; CHECK-NEXT:    [[TMP11]] = fadd fast double %"ld2'ipl4", [[TMP0_2]]
; CHECK-NEXT:    [[TMP12:%.*]] = insertvalue [3 x double] [[TMP9]], double [[TMP11]], 2
; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i64 [[IV_NEXT]], 10
; CHECK-NEXT:    br i1 [[CMP2]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret [3 x double] [[TMP12]]
;
