; Test the the loop nest depth is correctly calculated for basic blocks.

; REQUIRES: allow_dump

; Single threaded so that the dumps used for checking happen in order.
; RUN: %p2i --filetype=obj --disassemble -i %s --args -O2 --verbose=loop \
; RUN:     -log=%t --threads=0 && FileCheck %s < %t

define internal void @test_single_loop(i32 %a32) {
entry:
  %a = trunc i32 %a32 to i1
  br label %loop0

loop0:                               ; <-+
  br label %loop1                    ;   |
loop1:                               ;   |
  br i1 %a, label %loop0, label %out ; --+

out:
  ret void
}

; CHECK-LABEL: After loop analysis
; CHECK-NEXT: entry:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: loop0:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: loop1:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: out:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-LABEL: Before RMW

define internal void @test_single_loop_with_continue(i32 %a32, i32 %b32) {
entry:
  %a = trunc i32 %a32 to i1
  %b = trunc i32 %b32 to i1
  br label %loop0

loop0:                                 ; <-+
  br label %loop1                      ;   |
loop1:                                 ;   |
  br i1 %a, label %loop0, label %loop2 ; --+
loop2:                                 ;   |
  br i1 %b, label %loop0, label %out   ; --+

out:
  ret void
}

; CHECK-LABEL: After loop analysis
; CHECK-NEXT: entry:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: loop0:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: loop1:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: loop2:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: out:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-LABEL: Before RMW

define internal void @test_multiple_exits(i32 %a32, i32 %b32) {
entry:
  %a = trunc i32 %a32 to i1
  %b = trunc i32 %b32 to i1
  br label %loop0

loop0:                               ; <-+
  br label %loop1                    ;   |
loop1:                               ;   |
  br i1 %a, label %loop2, label %out ; --+-+
loop2:                               ;   | |
  br i1 %b, label %loop0, label %out ; --+ |
                                     ;     |
out:                                 ; <---+
  ret void
}

; CHECK-LABEL: After loop analysis
; CHECK-NEXT: entry:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: loop0:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: loop1:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: loop2:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: out:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-LABEL: Before RMW

define internal void @test_two_nested_loops(i32 %a32, i32 %b32) {
entry:
  %a = trunc i32 %a32 to i1
  %b = trunc i32 %b32 to i1
  br label %loop0_0

loop0_0:                                   ; <---+
  br label %loop1_0                        ;     |
loop1_0:                                   ; <-+ |
  br label %loop1_1                        ;   | |
loop1_1:                                   ;   | |
  br i1 %a, label %loop1_0, label %loop0_1 ; --+ |
loop0_1:                                   ;     |
  br i1 %b, label %loop0_0, label %out     ; ----+

out:
  ret void
}

; CHECK-LABEL: After loop analysis
; CHECK-NEXT: entry:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: loop0_0:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: loop1_0:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop1_1:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop0_1:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: out:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-LABEL: Before RMW

define internal void @test_two_nested_loops_with_continue(i32 %a32, i32 %b32,
                                                          i32 %c32) {
entry:
  %a = trunc i32 %a32 to i1
  %b = trunc i32 %b32 to i1
  %c = trunc i32 %c32 to i1
  br label %loop0_0

loop0_0:                                   ; <---+
  br label %loop1_0                        ;     |
loop1_0:                                   ; <-+ |
  br label %loop1_1                        ;   | |
loop1_1:                                   ;   | |
  br i1 %a, label %loop1_0, label %loop1_2 ; --+ |
loop1_2:                                   ;   | |
  br i1 %a, label %loop1_0, label %loop0_1 ; --+ |
loop0_1:                                   ;     |
  br i1 %b, label %loop0_0, label %out     ; ----+

out:
  ret void
}

; CHECK-LABEL: After loop analysis
; CHECK-NEXT: entry:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: loop0_0:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: loop1_0:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop1_1:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop1_2:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop0_1:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: out:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-LABEL: Before RMW

define internal void @test_multiple_nested_loops(i32 %a32, i32 %b32) {
entry:
  %a = trunc i32 %a32 to i1
  %b = trunc i32 %b32 to i1
  br label %loop0_0

loop0_0:                                   ; <---+
  br label %loop1_0                        ;     |
loop1_0:                                   ; <-+ |
  br label %loop1_1                        ;   | |
loop1_1:                                   ;   | |
  br i1 %a, label %loop1_0, label %loop0_1 ; --+ |
loop0_1:                                   ;     |
  br label %loop2_0                        ;     |
loop2_0:                                   ; <-+ |
  br label %loop2_1                        ;   | |
loop2_1:                                   ;   | |
  br i1 %a, label %loop2_0, label %loop0_2 ; --+ |
loop0_2:                                   ;     |
  br i1 %b, label %loop0_0, label %out     ; ----+

out:
  ret void
}

; CHECK-LABEL: After loop analysis
; CHECK-NEXT: entry:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: loop0_0:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: loop1_0:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop1_1:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop0_1:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: loop2_0:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop2_1:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop0_2:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: out:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-LABEL: Before RMW

define internal void @test_three_nested_loops(i32 %a32, i32 %b32, i32 %c32) {
entry:
  %a = trunc i32 %a32 to i1
  %b = trunc i32 %b32 to i1
  %c = trunc i32 %c32 to i1
  br label %loop0_0

loop0_0:                                   ; <-----+
  br label %loop1_0                        ;       |
loop1_0:                                   ; <---+ |
  br label %loop2_0                        ;     | |
loop2_0:                                   ; <-+ | |
  br label %loop2_1                        ;   | | |
loop2_1:                                   ;   | | |
  br i1 %a, label %loop2_0, label %loop1_1 ; --+ | |
loop1_1:                                   ;     | |
  br i1 %b, label %loop1_0, label %loop0_1 ; ----+ |
loop0_1:                                   ;       |
  br i1 %c, label %loop0_0, label %out     ; ------+

out:
  ret void
}

; CHECK-LABEL: After loop analysis
; CHECK-NEXT: entry:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: loop0_0:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: loop1_0:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop2_0:
; CHECK-NEXT: LoopNestDepth = 3
; CHECK-NEXT: loop2_1:
; CHECK-NEXT: LoopNestDepth = 3
; CHECK-NEXT: loop1_1:
; CHECK-NEXT: LoopNestDepth = 2
; CHECK-NEXT: loop0_1:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: out:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-LABEL: Before RMW

define internal void @test_diamond(i32 %a32) {
entry:
  %a = trunc i32 %a32 to i1
  br i1 %a, label %left, label %right

left:
  br label %out

right:
  br label %out

out:
  ret void
}

; CHECK-LABEL: After loop analysis
; CHECK-NEXT: entry:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: left:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: right:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: out:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-LABEL: Before RMW

define internal void @test_single_block_loop(i32 %count) {
entry:
  br label %body
body:
;  %i = phi i32 [ 0, %entry ], [ %inc, %body ]
; A normal loop would have a phi instruction like above for the induction
; variable, but that may introduce new basic blocks due to phi edge splitting,
; so we use an alternative definition for %i to make the test more clear.
  %i = add i32 %count, 1
  %inc = add i32 %i, 1
  %cmp = icmp slt i32 %inc, %count
  br i1 %cmp, label %body, label %exit
exit:
  ret void
}

; CHECK-LABEL: After loop analysis
; CHECK-NEXT: entry:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-NEXT: body:
; CHECK-NEXT: LoopNestDepth = 1
; CHECK-NEXT: exit:
; CHECK-NEXT: LoopNestDepth = 0
; CHECK-LABEL: Before RMW