; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -licm < %s | FileCheck %s

@G = external global i64

define void @test(i64 %n) {
; CHECK-LABEL: @test(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca i64
; CHECK-NEXT:    [[VAL:%.*]] = load i64, i64* [[A]]
; CHECK-NEXT:    store i64 [[VAL]], i64* @G
; 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:    [[EXITCOND:%.*]] = icmp ult i64 [[IV]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_BODY]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  %a = alloca i64
  %val = load i64, i64* %a
  store i64 %val, i64* @G
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp ult i64 %iv, %n
  br i1 %exitcond, label %for.body, label %exit
exit:
  ret void
}

define void @test2(i64 %n) {
; CHECK-LABEL: @test2(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca i64
; 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:    [[EXITCOND:%.*]] = icmp ult i64 [[IV]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_BODY]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    [[IV_LCSSA:%.*]] = phi i64 [ [[IV]], [[FOR_BODY]] ]
; CHECK-NEXT:    store i64 [[IV_LCSSA]], i64* [[A]], align 4
; CHECK-NEXT:    ret void
;
entry:
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  %a = alloca i64
  store i64 %iv, i64* %a
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp ult i64 %iv, %n
  br i1 %exitcond, label %for.body, label %exit
exit:
  ret void
}


define void @test3(i64 %n) {
; CHECK-LABEL: @test3(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca i64
; CHECK-NEXT:    [[A_I8:%.*]] = bitcast i64* [[A]] to i8*
; CHECK-NEXT:    [[A_OFFSET:%.*]] = getelementptr i8, i8* [[A_I8]], i64 4
; CHECK-NEXT:    store i8 0, i8* [[A_OFFSET]]
; 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:    [[EXITCOND:%.*]] = icmp ult i64 [[IV]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_BODY]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  %a = alloca i64
  %a.i8 = bitcast i64* %a to i8*
  %a.offset = getelementptr i8, i8* %a.i8, i64 4
  store i8 0, i8* %a.offset
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp ult i64 %iv, %n
  br i1 %exitcond, label %for.body, label %exit
exit:
  ret void
}

; This example is subtle.  Because the dynamic alloca isn't reclaimed until
; end of function scope, the captured value can legally point to a dynamic
; alloca stack region from a previous iteration.
define void @test4(i64 %n) {
; CHECK-LABEL: @test4(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[A:%.*]] = alloca i64
; CHECK-NEXT:    store i64 [[IV]], i64* [[A]]
; CHECK-NEXT:    call void @capture(i64* [[A]])
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ult i64 [[IV]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_BODY]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  %a = alloca i64
  store i64 %iv, i64* %a
  %a.i8 = bitcast i64* %a to i8*
  call void @capture(i64* %a)
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp ult i64 %iv, %n
  br i1 %exitcond, label %for.body, label %exit
exit:
  ret void
}
declare void @capture(i64* %a)


; TODO: not yet handled
define void @test5(i64 %n) {
; CHECK-LABEL: @test5(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
; CHECK:       for.body:
; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ [[IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[A:%.*]] = alloca i64
; CHECK-NEXT:    store i64 [[IV]], i64* [[A]]
; CHECK-NEXT:    [[A_I8:%.*]] = bitcast i64* [[A]] to i8*
; CHECK-NEXT:    [[TMP0:%.*]] = call {}* @llvm.invariant.start.p0i8(i64 8, i8* [[A_I8]])
; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ult i64 [[IV]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[FOR_BODY]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %for.body

for.body:
  %iv = phi i64 [ %iv.next, %for.body ], [ 0, %entry ]
  %a = alloca i64
  store i64 %iv, i64* %a
  %a.i8 = bitcast i64* %a to i8*
  call {}* @llvm.invariant.start.p0i8(i64 8, i8* %a.i8)
  %iv.next = add nuw nsw i64 %iv, 1
  %exitcond = icmp ult i64 %iv, %n
  br i1 %exitcond, label %for.body, label %exit
exit:
  ret void
}

declare {}* @llvm.invariant.start.p0i8(i64, i8* nocapture) nounwind readonly

