; Source:
; /*** for ***/
; void for_count()
; {
;     __attribute__((opencl_unroll_hint(1)))
;     for( int i = 0; i < 1024; ++i) {
;        if(i%2) continue;
;        int x = i;
;     }
; }
;
; /*** while ***/
; void while_count()
; {
;     int i = 1024;
;     __attribute__((opencl_unroll_hint(8)))
;     while(i-->0) {
;       if(i%2) continue;
;       int x = i;
;     }
; }
;
; /*** do ***/
; void do_count()
; {
;     int i = 1024;
;     __attribute__((opencl_unroll_hint))
;     do {
;       if(i%2) continue;
;       int x = i;
;    } while(i--> 0);
; }
;
; for_count_unusual() is a synthetically written function
;
; Command:
; clang -cc1 -triple spir64 -O0 LoopUnroll.cl -emit-llvm -o /test/SPIRV/transcoding/LoopUnroll.ll
;
; unroll_full() test was generated from the following source with -O2
; void foo();
;
; void unroll_full() {
;   #pragma clang unroll(full)
;   for (int i = 0; i != 1024; ++i) {
;     foo();
;   }
; }

; RUN: llvm-as < %s > %t.bc
; RUN: llvm-spirv %t.bc -o %t.spv
; RUN: llvm-spirv -to-text %t.spv -o %t.spt
; RUN: FileCheck < %t.spt %s --check-prefix=CHECK-SPIRV
; RUN: llvm-spirv -r -emit-opaque-pointers %t.spv -o %t.rev.bc
; RUN: llvm-dis %t.rev.bc -o - | FileCheck %s --check-prefixes=CHECK-LLVM
; RUN: llvm-spirv %t.bc -o %t.spv --spirv-max-version=1.1
; RUN: llvm-spirv -to-text %t.spv -o %t.spt
; RUN: FileCheck < %t.spt %s --check-prefix=CHECK-SPIRV-NEGATIVE

; Check SPIR-V versions in a format magic number + version
; CHECK-SPIRV: 119734787 66560
; CHECK-SPIRV-NEGATIVE: 119734787 65536

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
target triple = "spir64"

; CHECK-SPIRV: Function
; Function Attrs: noinline nounwind optnone
define spir_func void @for_count() #0 {
entry:
; CHECK-SPIRV: Label
  %i = alloca i32, align 4
  %x = alloca i32, align 4
  store i32 0, i32* %i, align 4
  br label %for.cond

for.cond:                                         ; preds = %for.inc, %entry
; CHECK-SPIRV: Label [[#HEADER:]]
  %0 = load i32, i32* %i, align 4
  %cmp = icmp slt i32 %0, 1024
; Per SPIRV spec p3.23 "DontUnroll" loop control = 0x2
; CHECK-SPIRV: LoopMerge [[#MERGEBLOCK:]] [[#CONTINUE:]] 2
; CHECK-SPIRV: BranchConditional [[#]] [[#]] [[#MERGEBLOCK]]
  br i1 %cmp, label %for.body, label %for.end

for.body:                                         ; preds = %for.cond
; CHECK-SPIRV: Label
  %1 = load i32, i32* %i, align 4
  %rem = srem i32 %1, 2
  %tobool = icmp ne i32 %rem, 0
  br i1 %tobool, label %if.then, label %if.end

if.then:                                          ; preds = %for.body
; CHECK-SPIRV: Label
  br label %for.inc

if.end:                                           ; preds = %for.body
; CHECK-SPIRV: Label
  %2 = load i32, i32* %i, align 4
  store i32 %2, i32* %x, align 4
  br label %for.inc

for.inc:                                          ; preds = %if.end, %if.then
; CHECK-SPIRV: Label [[#CONTINUE]]
  %3 = load i32, i32* %i, align 4
  %inc = add nsw i32 %3, 1
  store i32 %inc, i32* %i, align 4
  br label %for.cond, !llvm.loop !5
; CHECK-LLVM: br label %for.cond, !llvm.loop ![[#UNROLLDISABLE:]]
; CHECK-SPIRV: Branch [[#HEADER]]

for.end:                                          ; preds = %for.cond
; CHECK-SPIRV: Label [[#MERGEBLOCK]]
  ret void
}

; CHECK-SPIRV: Function
; Function Attrs: noinline nounwind optnone
define spir_func void @while_count() #0 {
entry:
; CHECK-SPIRV: Label
  %i = alloca i32, align 4
  %x = alloca i32, align 4
  store i32 1024, i32* %i, align 4
  br label %while.cond

while.cond:                                       ; preds = %if.end, %if.then, %entry
; CHECK-SPIRV: Label [[#HEADER:]]
  %0 = load i32, i32* %i, align 4
  %dec = add nsw i32 %0, -1
  store i32 %dec, i32* %i, align 4
  %cmp = icmp sgt i32 %0, 0
; Per SPIRV spec p3.23 "Unroll" loop control = 0x1
; CHECK-SPIRV: LoopMerge [[#MERGEBLOCK:]] [[#CONTINUE:]] 256 8
; CHECK-SPIRV: BranchConditional [[#]] [[#]] [[#MERGEBLOCK]]
; CHECK-SPIRV-NEGATIVE-NOT: LoopMerge {{.*}} 256
  br i1 %cmp, label %while.body, label %while.end

while.body:                                       ; preds = %while.cond
; CHECK-SPIRV: Label
  %1 = load i32, i32* %i, align 4
  %rem = srem i32 %1, 2
  %tobool = icmp ne i32 %rem, 0
  br i1 %tobool, label %if.then, label %if.end

if.then:                                          ; preds = %while.body
; CHECK-SPIRV: Label
; CHECK-LLVM: br label %while.cond, !llvm.loop ![[#UNROLLCOUNT:]]
  br label %while.cond, !llvm.loop !7

; loop-simplify pass will create extra basic block which is the only one in
; loop having a back-edge to the header
; CHECK-SPIRV: [[#CONTINUE]]
; CHECK-SPIRV: Branch [[#HEADER]]

if.end:                                           ; preds = %while.body
; CHECK-SPIRV: Label
  %2 = load i32, i32* %i, align 4
  store i32 %2, i32* %x, align 4
  br label %while.cond, !llvm.loop !7

while.end:                                        ; preds = %while.cond
; CHECK-SPIRV: [[#MERGEBLOCK]]
  ret void
}

; CHECK-SPIRV: Function
; Function Attrs: noinline nounwind optnone
define spir_func void @do_count() #0 {
entry:
; CHECK-SPIRV: Label
  %i = alloca i32, align 4
  %x = alloca i32, align 4
  store i32 1024, i32* %i, align 4
  br label %do.body

do.body:                                          ; preds = %do.cond, %entry
; CHECK-SPIRV: Label [[#HEADER:]]
  %0 = load i32, i32* %i, align 4
  %rem = srem i32 %0, 2
  %tobool = icmp ne i32 %rem, 0
; Per SPIRV spec p3.23 "Unroll" loop control = 0x1
; CHECK-SPIRV: LoopMerge [[#MERGEBLOCK:]] [[#CONTINUE:]] 1
; CHECK-SPIRV: BranchConditional
  br i1 %tobool, label %if.then, label %if.end

if.then:                                          ; preds = %do.body
; CHECK-SPIRV: Label
  br label %do.cond

if.end:                                           ; preds = %do.body
; CHECK-SPIRV: Label
  %1 = load i32, i32* %i, align 4
  store i32 %1, i32* %x, align 4
  br label %do.cond

do.cond:                                          ; preds = %if.end, %if.then
; CHECK-SPIRV: Label [[#CONTINUE]]
  %2 = load i32, i32* %i, align 4
  %dec = add nsw i32 %2, -1
  store i32 %dec, i32* %i, align 4
  %cmp = icmp sgt i32 %2, 0
; CHECK-SPIRV: BranchConditional [[#]] [[#HEADER]] [[#MERGEBLOCK]]
; CHECK-LLVM: br i1 %cmp, label %do.body, label %do.end, !llvm.loop ![[#UNROLLENABLE1:]]
  br i1 %cmp, label %do.body, label %do.end, !llvm.loop !9

do.end:                                           ; preds = %do.cond
; CHECK-SPIRV: Label [[#MERGEBLOCK]]
  ret void
}

; CHECK-SPIRV: Function
; Function Attrs: noinline nounwind optnone
define spir_func void @for_count_unusual() #0 {
entry:
; CHECK-SPIRV: Label
  %i = alloca i32, align 4
  %x = alloca i32, align 4
  store i32 1024, i32* %i, align 4
  br label %for.body

for.body:                                          ; preds = %for.cond, %entry
; CHECK-SPIRV: Label [[#HEADER:]]
  %0 = load i32, i32* %i, align 4
  %rem = srem i32 %0, 2
  %tobool = icmp ne i32 %rem, 0
; Per SPIRV spec p3.23 "Unroll" loop control = 0x1
; CHECK-SPIRV: LoopMerge [[#MERGEBLOCK:]] [[#CONTINUE:]] 1
; CHECK-SPIRV: BranchConditional
  br i1 %tobool, label %if.then, label %if.end

if.then:                                          ; preds = %for.body
; CHECK-SPIRV: Label
  br label %for.cond

if.end:                                           ; preds = %for.body
; CHECK-SPIRV: Label
  %1 = load i32, i32* %i, align 4
  store i32 %1, i32* %x, align 4
  br label %for.cond

for.cond:                                          ; preds = %if.end, %if.then
; CHECK-SPIRV: Label [[#CONTINUE]]
  %2 = load i32, i32* %i, align 4
  %dec = add nsw i32 %2, -1
  store i32 %dec, i32* %i, align 4
  %cmp = icmp sgt i32 %2, 0
; CHECK-SPIRV: BranchConditional [[#]] [[#MERGEBLOCK]] [[#HEADER]]
; CHECK-LLVM: br i1 %cmp, label %for.end, label %for.body, !llvm.loop ![[#UNROLLENABLE2:]]
  br i1 %cmp, label %for.end, label %for.body, !llvm.loop !9

for.end:                                           ; preds = %for.cond
; CHECK-SPIRV: Label [[#MERGEBLOCK]]
  ret void
}

; CHECK-SPIRV: Function
; CHECK-SPIRV: Label
; CHECK-SPIRV: Branch [[#Header:]]
; CHECK-SPIRV: LoopMerge [[#Return:]] [[#Header]] 257 1
; CHECK-SPIRV: BranchConditional [[#]] [[#Return]] [[#Header]]
; CHECK-SPIRV: Label [[#Return]]
; Function Attrs: noinline nounwind optnone
define spir_func void @unroll_full() {
  br label %2

1:                                                ; preds = %2
  ret void

2:                                                ; preds = %0, %2
  %3 = phi i32 [ 0, %0 ], [ %4, %2 ]
  tail call void @_Z3foov()
  %4 = add nuw nsw i32 %3, 1
  %5 = icmp eq i32 %4, 1024
  ; CHECK-LLVM: br i1 %[[#]], label %[[#]], label %[[#]], !llvm.loop ![[#FULL:]]
  br i1 %5, label %1, label %2, !llvm.loop !11
}

declare void @_Z3foov()

attributes #0 = { noinline nounwind optnone "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" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!opencl.enable.FP_CONTRACT = !{}
!opencl.ocl.version = !{!1}
!opencl.spir.version = !{!2}
!opencl.used.extensions = !{!3}
!opencl.used.optional.core.features = !{!3}
!opencl.compiler.options = !{!3}
!llvm.ident = !{!4}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 1, i32 0}
!2 = !{i32 1, i32 2}
!3 = !{}
!4 = !{!"clang version 5.0.1 (cfe/trunk)"}
!5 = distinct !{!5, !6}
!6 = !{!"llvm.loop.unroll.disable"}
!7 = distinct !{!7, !8}
!8 = !{!"llvm.loop.unroll.count", i32 8}
!9 = distinct !{!9, !10}
!10 = !{!"llvm.loop.unroll.enable"}
!11 = distinct !{!11, !12}
!12 = !{!"llvm.loop.unroll.full"}

; CHECK-LLVM: ![[#UNROLLDISABLE]] = distinct !{![[#UNROLLDISABLE]], ![[#DISABLE:]]}
; CHECK-LLVM: ![[#DISABLE]] = !{!"llvm.loop.unroll.disable"}
; CHECK-LLVM: ![[#UNROLLCOUNT]] = distinct !{![[#UNROLLCOUNT]], ![[#COUNT:]]}
; CHECK-LLVM: ![[#COUNT]] = !{!"llvm.loop.unroll.count", i32 8}
; CHECK-LLVM: ![[#UNROLLENABLE1]] = distinct !{![[#UNROLLENABLE1]], ![[#ENABLE:]]}
; CHECK-LLVM: ![[#ENABLE]] = !{!"llvm.loop.unroll.enable"}
; CHECK-LLVM: ![[#UNROLLENABLE2]] = distinct !{![[#UNROLLENABLE2]], ![[#ENABLE]]}
; CHECK-LLVM: ![[#FULL]] = distinct !{![[#FULL]], ![[#UNROLLFULL:]]}
; CHECK-LLVM: ![[#UNROLLFULL]] = !{!"llvm.loop.unroll.full"}
