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

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

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

; Function Attrs: noinline nounwind uwtable
define dso_local void @f(double* %x, double** %y, i64 %n) #0 {
entry:
  br label %for.cond

for.cond:                                         ; preds = %for.body, %entry
  %i.0 = phi i64 [ 0, %entry ], [ %inc, %for.body ]
  %cmp = icmp ule i64 %i.0, %n
  br i1 %cmp, label %for.body, label %for.end

for.body:                                         ; preds = %for.cond
  %arrayidx = getelementptr inbounds double, double* %x, i64 0
  %0 = load double, double* %arrayidx
  %1 = load double*, double** %y
  %2 = load double, double* %1
  %add = fadd fast double %2, %0
  store double %add, double* %1
  %inc = add i64 %i.0, 1
  br label %for.cond

for.end:                                          ; preds = %for.cond
  ret void
}

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

attributes #0 = { noinline nounwind uwtable }

; CHECK: define {{[^@]+}}@fwddiffe3f(double* nocapture readonly [[X:%.*]], [3 x double*] %"x'", double** nocapture readonly [[Y:%.*]], [3 x double**] %"y'", i64 [[N:%.*]])
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP0:%.*]] = add {{(nuw )?}}i64 [[N]], 1
; CHECK-NEXT:    br label [[FOR_COND:%.*]]
; CHECK:       for.cond:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY:%.*]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i64 [[IV]], [[TMP0]]
; CHECK-NEXT:    br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_END:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[TMP1:%.*]] = extractvalue [3 x double*] %"x'", 0
; CHECK-NEXT:    [[TMP2:%.*]] = extractvalue [3 x double*] %"x'", 1
; CHECK-NEXT:    [[TMP3:%.*]] = extractvalue [3 x double*] %"x'", 2
; CHECK-NEXT:    %"'ipl" = load double, double* [[TMP1]]
; CHECK-NEXT:    %"'ipl5" = load double, double* [[TMP2]]
; CHECK-NEXT:    %"'ipl6" = load double, double* [[TMP3]]
; CHECK-NEXT:    [[TMP4:%.*]] = load double, double* [[X]]
; CHECK-NEXT:    [[TMP5:%.*]] = extractvalue [3 x double**] %"y'", 0
; CHECK-NEXT:    %"'ipl7" = load double*, double** [[TMP5]]
; CHECK-NEXT:    [[TMP6:%.*]] = extractvalue [3 x double**] %"y'", 1
; CHECK-NEXT:    %"'ipl8" = load double*, double** [[TMP6]]
; CHECK-NEXT:    [[TMP7:%.*]] = extractvalue [3 x double**] %"y'", 2
; CHECK-NEXT:    %"'ipl9" = load double*, double** [[TMP7]]
; CHECK-NEXT:    [[TMP8:%.*]] = load double*, double** [[Y]]
; CHECK-NEXT:    %"'ipl10" = load double, double* %"'ipl7"
; CHECK-NEXT:    %"'ipl11" = load double, double* %"'ipl8"
; CHECK-NEXT:    %"'ipl12" = load double, double* %"'ipl9"
; CHECK-NEXT:    [[TMP9:%.*]] = load double, double* [[TMP8]]
; CHECK-NEXT:    [[ADD:%.*]] = fadd fast double [[TMP9]], [[TMP4]]
; CHECK-NEXT:    [[TMP10:%.*]] = fadd fast double %"'ipl10", %"'ipl"
; CHECK-NEXT:    [[TMP11:%.*]] = fadd fast double %"'ipl11", %"'ipl5"
; CHECK-NEXT:    [[TMP12:%.*]] = fadd fast double %"'ipl12", %"'ipl6"
; CHECK-NEXT:    store double [[TMP10]], double* %"'ipl7"
; CHECK-NEXT:    store double [[TMP11]], double* %"'ipl8"
; CHECK-NEXT:    store double [[TMP12]], double* %"'ipl9"
; CHECK-NEXT:    store double [[ADD]], double* [[TMP8]]
; CHECK-NEXT:    br label [[FOR_COND]]
; CHECK:       for.end:
; CHECK-NEXT:    ret void
;
