; RUN: opt -passes='print<access-info>' -disable-output  < %s 2>&1 | FileCheck %s

%s1 = type { [32000 x double], [32000 x double], [32000 x double] }

define i32 @load_with_pointer_phi_no_runtime_checks(ptr %data) {
; CHECK-LABEL: load_with_pointer_phi_no_runtime_checks
; CHECK-NEXT:  loop.header:
; CHECK-NEXT:    Memory dependences are safe
;
entry:
  br label %loop.header

loop.header:                                        ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %iv.next = add nuw nsw i64 %iv, 1
  %cmp5 = icmp ult i64 %iv, 15999
  %arrayidx = getelementptr inbounds %s1, ptr %data, i64 0, i32 0, i64 %iv
  br i1 %cmp5, label %if.then, label %if.else

if.then:                                          ; preds = %loop.header
  %gep.1 = getelementptr inbounds %s1, ptr %data, i64 0, i32 1, i64 %iv
  br label %loop.latch

if.else:                                          ; preds = %loop.header
  %gep.2 = getelementptr inbounds %s1, ptr %data, i64 0, i32 2, i64 %iv
  br label %loop.latch

loop.latch:                                          ; preds = %if.else, %if.then
  %gep.2.sink = phi ptr [ %gep.2, %if.else ], [ %gep.1, %if.then ]
  %v8 = load double, ptr %gep.2.sink, align 8
  %mul16 = fmul double 3.0, %v8
  store double %mul16, ptr %arrayidx, align 8
  %exitcond.not = icmp eq i64 %iv.next, 32000
  br i1 %exitcond.not, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret i32 10
}

define i32 @store_with_pointer_phi_no_runtime_checks(ptr %data) {
; CHECK-LABEL: 'store_with_pointer_phi_no_runtime_checks'
; CHECK-NEXT:  loop.header:
; CHECK-NEXT:    Memory dependences are safe
;
entry:
  br label %loop.header

loop.header:                                        ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %iv.next = add nuw nsw i64 %iv, 1
  %cmp5 = icmp ult i64 %iv, 15999
  %arrayidx = getelementptr inbounds %s1, ptr %data, i64 0, i32 0, i64 %iv
  br i1 %cmp5, label %if.then, label %if.else

if.then:                                          ; preds = %loop.header
  %gep.1 = getelementptr inbounds %s1, ptr %data, i64 0, i32 1, i64 %iv
  br label %loop.latch

if.else:                                          ; preds = %loop.header
  %gep.2 = getelementptr inbounds %s1, ptr %data, i64 0, i32 2, i64 %iv
  br label %loop.latch

loop.latch:                                          ; preds = %if.else, %if.then
  %gep.2.sink = phi ptr [ %gep.2, %if.else ], [ %gep.1, %if.then ]
  %v8 = load double, ptr %arrayidx, align 8
  %mul16 = fmul double 3.0, %v8
  store double %mul16, ptr %gep.2.sink, align 8
  %exitcond.not = icmp eq i64 %iv.next, 32000
  br i1 %exitcond.not, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret i32 10
}

define i32 @store_with_pointer_phi_runtime_checks(ptr %A, ptr %B, ptr %C) {
; CHECK-LABEL: 'store_with_pointer_phi_runtime_checks'
; CHECK-NEXT:  loop.header:
; CHECK-NEXT:    Memory dependences are safe with run-time checks
; CHECK:         Run-time memory checks:
; CHECK-NEXT:    Check 0:
; CHECK-NEXT:      Comparing group ([[GROUP_B:.+]]):
; CHECK-NEXT:        %gep.1 = getelementptr inbounds double, ptr %B, i64 %iv
; CHECK-NEXT:      Against group ([[GROUP_C:.+]]):
; CHECK-NEXT:        %gep.2 = getelementptr inbounds double, ptr %C, i64 %iv
; CHECK-NEXT:    Check 1:
; CHECK-NEXT:      Comparing group ([[GROUP_B]]):
; CHECK-NEXT:        %gep.1 = getelementptr inbounds double, ptr %B, i64 %iv
; CHECK-NEXT:      Against group ([[GROUP_A:.+]]):
; CHECK-NEXT:        %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
; CHECK-NEXT:    Check 2:
; CHECK-NEXT:      Comparing group ([[GROUP_C]]):
; CHECK-NEXT:        %gep.2 = getelementptr inbounds double, ptr %C, i64 %iv
; CHECK-NEXT:      Against group ([[GROUP_A]]):
; CHECK-NEXT:        %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
;
entry:
  br label %loop.header

loop.header:                                        ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %iv.next = add nuw nsw i64 %iv, 1
  %cmp5 = icmp ult i64 %iv, 15999
  %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
  br i1 %cmp5, label %if.then, label %if.else

if.then:                                          ; preds = %loop.header
  %gep.1 = getelementptr inbounds double, ptr %B, i64 %iv
  br label %loop.latch

if.else:                                          ; preds = %loop.header
  %gep.2 = getelementptr inbounds double, ptr %C, i64 %iv
  br label %loop.latch

loop.latch:                                          ; preds = %if.else, %if.then
  %gep.2.sink = phi ptr [ %gep.2, %if.else ], [ %gep.1, %if.then ]
  %v8 = load double, ptr %arrayidx, align 8
  %mul16 = fmul double 3.0, %v8
  store double %mul16, ptr %gep.2.sink, align 8
  %exitcond.not = icmp eq i64 %iv.next, 32000
  br i1 %exitcond.not, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret i32 10
}

define i32 @load_with_pointer_phi_outside_loop(ptr %A, ptr %B, ptr %C, i1 %c.0, i1 %c.1) {
; CHECK-LABEL: 'load_with_pointer_phi_outside_loop'
; CHECK-NEXT:  loop.header:
; CHECK-NEXT:    Report: unsafe dependent memory operations in loop
; CHECK-NEXT:    Unknown data dependence.
; CHECK-NEXT:    Dependences:
; CHECK-NEXT:      Unknown:
; CHECK-NEXT:          %v8 = load double, ptr %ptr, align 8 ->
; CHECK-NEXT:          store double %mul16, ptr %arrayidx, align 8
;
entry:
  br i1 %c.0, label %if.then, label %if.else

if.then:
  br label %loop.ph

if.else:
  %ptr.select = select i1 %c.1, ptr %C, ptr %B
  br label %loop.ph

loop.ph:
  %ptr = phi ptr [ %A, %if.then ], [ %ptr.select, %if.else ]
  br label %loop.header

loop.header:                                        ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %loop.ph ], [ %iv.next, %loop.header ]
  %iv.next = add nuw nsw i64 %iv, 1
  %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
  %v8 = load double, ptr %ptr, align 8
  %mul16 = fmul double 3.0, %v8
  store double %mul16, ptr %arrayidx, align 8
  %exitcond.not = icmp eq i64 %iv.next, 32000
  br i1 %exitcond.not, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret i32 10
}

define i32 @store_with_pointer_phi_outside_loop(ptr %A, ptr %B, ptr %C, i1 %c.0, i1 %c.1) {
; CHECK-LABEL: 'store_with_pointer_phi_outside_loop'
; CHECK-NEXT:  loop.header:
; CHECK-NEXT:    Report: unsafe dependent memory operations in loop.
; CHECK-NEXT:    Unknown data dependence.
; CHECK-NEXT:    Dependences:
; CHECK-NEXT:      Unknown:
; CHECK-NEXT:          %v8 = load double, ptr %arrayidx, align 8 ->
; CHECK-NEXT:          store double %mul16, ptr %ptr, align 8
;
entry:
  br i1 %c.0, label %if.then, label %if.else

if.then:
  br label %loop.ph

if.else:
  %ptr.select = select i1 %c.1, ptr %C, ptr %B
  br label %loop.ph

loop.ph:
  %ptr = phi ptr [ %A, %if.then ], [ %ptr.select, %if.else ]
  br label %loop.header

loop.header:                                        ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %loop.ph ], [ %iv.next, %loop.header ]
  %iv.next = add nuw nsw i64 %iv, 1
  %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
  %v8 = load double, ptr %arrayidx, align 8
  %mul16 = fmul double 3.0, %v8
  store double %mul16, ptr %ptr, align 8
  %exitcond.not = icmp eq i64 %iv.next, 32000
  br i1 %exitcond.not, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret i32 10
}

define i32 @store_with_pointer_phi_incoming_phi(ptr %A, ptr %B, ptr %C, i1 %c.0, i1 %c.1) {
; CHECK-LABEL: 'store_with_pointer_phi_incoming_phi'
; CHECK-NEXT:  loop.header:
; CHECK-NEXT:    Report: unsafe dependent memory operations in loop. Use #pragma loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
; CHECK-NEXT:    Unknown data dependence.
; CHECK-NEXT:    Dependences:
; CHECK-NEXT:      Unknown:
; CHECK-NEXT:          %v8 = load double, ptr %arrayidx, align 8 ->
; CHECK-NEXT:          store double %mul16, ptr %ptr.2, align 8
; CHECK-EMPTY:
; CHECK-NEXT:    Run-time memory checks:
; CHECK-NEXT:    Check 0:
; CHECK-NEXT:      Comparing group ([[GROUP_C:.+]]):
; CHECK-NEXT:      ptr %C
; CHECK-NEXT:      Against group ([[GROUP_B:.+]]):
; CHECK-NEXT:      ptr %B
; CHECK-NEXT:    Check 1:
; CHECK-NEXT:      Comparing group ([[GROUP_C]]):
; CHECK-NEXT:      ptr %C
; CHECK-NEXT:      Against group ([[GROUP_A:.+]]):
; CHECK-NEXT:        %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
; CHECK-NEXT:      ptr %A
; CHECK-NEXT:    Check 2:
; CHECK-NEXT:      Comparing group ([[GROUP_B]]):
; CHECK-NEXT:      ptr %B
; CHECK-NEXT:      Against group ([[GROUP_A]]):
; CHECK-NEXT:        %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
; CHECK-NEXT:      ptr %A
; CHECK-NEXT:    Grouped accesses:
; CHECK-NEXT:      Group [[GROUP_C]]:
; CHECK-NEXT:        (Low: %C High: (8 + %C))
; CHECK-NEXT:          Member: %C
; CHECK-NEXT:      Group [[GROUP_B]]:
; CHECK-NEXT:        (Low: %B High: (8 + %B))
; CHECK-NEXT:          Member: %B
; CHECK-NEXT:      Group [[GROUP_A]]:
; CHECK-NEXT:        (Low: %A High: (256000 + %A))
; CHECK-NEXT:          Member: {%A,+,8}<nuw><%loop.header>
; CHECK-NEXT:          Member: %A
; CHECK-EMPTY
entry:
  br label %loop.header

loop.header:                                        ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %iv.next = add nuw nsw i64 %iv, 1
  %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
  %v8 = load double, ptr %arrayidx, align 8
  %mul16 = fmul double 3.0, %v8
  br i1 %c.0, label %loop.then, label %loop.latch

loop.then:
  br i1 %c.0, label %loop.then.2, label %loop.else.2

loop.then.2:
  br label %merge.2

loop.else.2:
  br label %merge.2


merge.2:
  %ptr = phi ptr [ %A, %loop.then.2 ], [ %B, %loop.else.2 ]
  br label %loop.latch


loop.latch:
  %ptr.2 = phi ptr [ %ptr, %merge.2], [ %C, %loop.header ]
  store double %mul16, ptr %ptr.2, align 8
  %exitcond.not = icmp eq i64 %iv.next, 32000
  br i1 %exitcond.not, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret i32 10
}

; Test cases with pointer phis forming a cycle.
define i32 @store_with_pointer_phi_incoming_phi_irreducible_cycle(ptr %A, ptr %B, ptr %C, i1 %c.0, i1 %c.1) {
; CHECK-LABEL: 'store_with_pointer_phi_incoming_phi_irreducible_cycle'
; CHECK-NEXT:  loop.header:
; CHECK-NEXT:    Report: unsafe dependent memory operations in loop. Use #pragma loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
; CHECK-NEXT:    Unknown data dependence.
; CHECK-NEXT:    Dependences:
; CHECK-NEXT:      Unknown:
; CHECK-NEXT:          %v8 = load double, ptr %arrayidx, align 8 ->
; CHECK-NEXT:          store double %mul16, ptr %ptr.3, align 8
; CHECK-EMPTY:
; CHECK-NEXT:    Run-time memory checks:
; CHECK-NEXT:    Check 0:
; CHECK-NEXT:      Comparing group ([[GROUP_C:.+]]):
; CHECK-NEXT:      ptr %C
; CHECK-NEXT:      Against group ([[GROUP_B:.+]]):
; CHECK-NEXT:      ptr %B
; CHECK-NEXT:    Check 1:
; CHECK-NEXT:      Comparing group ([[GROUP_C]]):
; CHECK-NEXT:      ptr %C
; CHECK-NEXT:      Against group ([[GROUP_A:.+]]):
; CHECK-NEXT:        %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
; CHECK-NEXT:      ptr %A
; CHECK-NEXT:    Check 2:
; CHECK-NEXT:      Comparing group ([[GROUP_B]]):
; CHECK-NEXT:      ptr %B
; CHECK-NEXT:      Against group ([[GROUP_A]]):
; CHECK-NEXT:        %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
; CHECK-NEXT:      ptr %A
; CHECK-NEXT:    Grouped accesses:
; CHECK-NEXT:      Group [[GROUP_C]]
; CHECK-NEXT:        (Low: %C High: (8 + %C))
; CHECK-NEXT:          Member: %C
; CHECK-NEXT:      Group [[GROUP_B]]
; CHECK-NEXT:        (Low: %B High: (8 + %B))
; CHECK-NEXT:          Member: %B
; CHECK-NEXT:      Group [[GROUP_A]]
; CHECK-NEXT:        (Low: %A High: (256000 + %A))
; CHECK-NEXT:          Member: {%A,+,8}<nuw><%loop.header>
; CHECK-NEXT:          Member: %A
; CHECK-EMPTY
entry:
  br label %loop.header

loop.header:                                        ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %iv.next = add nuw nsw i64 %iv, 1
  %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
  %v8 = load double, ptr %arrayidx, align 8
  %mul16 = fmul double 3.0, %v8
  br i1 %c.0, label %loop.then, label %loop.latch

loop.then:
  br i1 %c.0, label %BB.A, label %BB.B

BB.A:
  %ptr = phi ptr [ %A, %loop.then ], [ %ptr.2, %BB.B ]
  br label %BB.B

BB.B:
  %ptr.2 = phi ptr [ %ptr, %BB.A ], [ %B, %loop.then ]
  br i1 %c.1, label %loop.latch, label %BB.A

loop.latch:
  %ptr.3 = phi ptr [ %ptr.2, %BB.B ], [ %C, %loop.header ]
  store double %mul16, ptr %ptr.3, align 8
  %exitcond.not = icmp eq i64 %iv.next, 32000
  br i1 %exitcond.not, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret i32 10
}

define i32 @store_with_pointer_phi_outside_loop_select(ptr %A, ptr %B, ptr %C, i1 %c.0, i1 %c.1) {
; CHECK-LABEL: 'store_with_pointer_phi_outside_loop_select'
; CHECK-NEXT:  loop.header:
; CHECK-NEXT:    Report: unsafe dependent memory operations in loop.
; CHECK-NEXT:    Unknown data dependence.
; CHECK-NEXT:    Dependences:
; CHECK-NEXT:      Unknown:
; CHECK-NEXT:          %v8 = load double, ptr %arrayidx, align 8 ->
; CHECK-NEXT:          store double %mul16, ptr %ptr, align 8
;
entry:
  br i1 %c.0, label %if.then, label %if.else

if.then:
  br label %loop.ph

if.else:
  %ptr.select = select i1 %c.1, ptr %C, ptr %B
  br label %loop.ph

loop.ph:
  %ptr = phi ptr [ %A, %if.then ], [ %ptr.select, %if.else ]
  br label %loop.header

loop.header:                                        ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %loop.ph ], [ %iv.next, %loop.header ]
  %iv.next = add nuw nsw i64 %iv, 1
  %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
  %v8 = load double, ptr %arrayidx, align 8
  %mul16 = fmul double 3.0, %v8
  store double %mul16, ptr %ptr, align 8
  %exitcond.not = icmp eq i64 %iv.next, 32000
  br i1 %exitcond.not, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret i32 10
}

define i32 @store_with_pointer_phi_in_same_bb_use_other_phi(ptr %A, ptr %B, ptr %C, ptr %D, i1 %c.0, i1 %c.1) {
; CHECK-LABEL: Loop access info in function 'store_with_pointer_phi_in_same_bb_use_other_phi':
; CHECK-NEXT:   loop.header:
; CHECK-NEXT:     Report: cannot identify array bounds
; CHECK-NEXT:     Dependences:
; CHECK-NEXT:     Run-time memory checks:
; CHECK-NEXT:     Grouped accesses:
; CHECK-EMPTY:
;
entry:
  br label %loop.header

loop.header:                                        ; preds = %loop.latch, %entry
  %ptr.0 = phi ptr [ %C, %entry ], [ %D, %loop.header ]
  %ptr.1 = phi ptr [ %B, %entry ], [ %ptr.0, %loop.header ]
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.header ]
  %iv.next = add nuw nsw i64 %iv, 1
  %arrayidx = getelementptr inbounds double, ptr %A, i64 %iv
  %v8 = load double, ptr %arrayidx, align 8
  %mul16 = fmul double 3.0, %v8
  store double %mul16, ptr %ptr.1, align 8
  %exitcond.not = icmp eq i64 %iv.next, 32000
  br i1 %exitcond.not, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret i32 10
}

define void @phi_load_store_memdep_check(i1 %c, ptr %A, ptr %B, ptr %C) {
; CHECK-LABEL: Loop access info in function 'phi_load_store_memdep_check':
; CHECK-NEXT:   for.body:
; CHECK-NEXT:    Report: unsafe dependent memory operations in loop. Use #pragma loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
; CHECK-NEXT:    Unknown data dependence.
; CHECK-NEXT:    Dependences:
; CHECK-NEXT:      Unknown:
; CHECK-NEXT:          %lv3 = load i16, ptr %c.sink, align 2 ->
; CHECK-NEXT:          store i16 %add, ptr %c.sink, align 1
; CHECK-EMPTY:
; CHECK-NEXT:      Unknown:
; CHECK-NEXT:          %lv3 = load i16, ptr %c.sink, align 2 ->
; CHECK-NEXT:          store i16 %add, ptr %c.sink, align 1
; CHECK-EMPTY:
; CHECK-NEXT:      Unknown:
; CHECK-NEXT:          %lv = load i16, ptr %A, align 1 ->
; CHECK-NEXT:          store i16 %lv, ptr %A, align 1
; CHECK-EMPTY:
; CHECK-NEXT:      Unknown:
; CHECK-NEXT:          store i16 %lv, ptr %A, align 1 ->
; CHECK-NEXT:          %lv2 = load i16, ptr %A, align 1
; CHECK-EMPTY:
; CHECK-NEXT:    Run-time memory checks:
; CHECK-NEXT:    Check 0:
; CHECK-NEXT:      Comparing group ([[GROUP_A:.+]]):
; CHECK-NEXT:      ptr %A
; CHECK-NEXT:      ptr %A
; CHECK-NEXT:      Against group ([[GROUP_C:.+]]):
; CHECK-NEXT:      ptr %C
; CHECK-NEXT:      ptr %C
; CHECK-NEXT:    Check 1:
; CHECK-NEXT:      Comparing group ([[GROUP_A]]):
; CHECK-NEXT:      ptr %A
; CHECK-NEXT:      ptr %A
; CHECK-NEXT:      Against group ([[GROUP_B:.+]]):
; CHECK-NEXT:      ptr %B
; CHECK-NEXT:      ptr %B
; CHECK-NEXT:    Check 2:
; CHECK-NEXT:      Comparing group ([[GROUP_C]]):
; CHECK-NEXT:      ptr %C
; CHECK-NEXT:      ptr %C
; CHECK-NEXT:      Against group ([[GROUP_B]]):
; CHECK-NEXT:      ptr %B
; CHECK-NEXT:      ptr %B
; CHECK-NEXT:    Grouped accesses:
; CHECK-NEXT:      Group [[GROUP_A]]
; CHECK-NEXT:        (Low: %A High: (2 + %A))
; CHECK-NEXT:          Member: %A
; CHECK-NEXT:          Member: %A
; CHECK-NEXT:      Group [[GROUP_C]]
; CHECK-NEXT:        (Low: %C High: (2 + %C))
; CHECK-NEXT:          Member: %C
; CHECK-NEXT:          Member: %C
; CHECK-NEXT:      Group [[GROUP_B]]
; CHECK-NEXT:        (Low: %B High: (2 + %B))
; CHECK-NEXT:          Member: %B
; CHECK-NEXT:          Member: %B
; CHECK-EMPTY:
;
entry:
  br label %for.body

for.body:                                         ; preds = %if.end, %entry
  %iv = phi i16 [ 0, %entry ], [ %iv.next, %if.end ]
  %lv = load i16, ptr %A, align 1
  store i16 %lv, ptr %A, align 1
  br i1 %c, label %if.then, label %if.end

if.then:                                          ; preds = %for.body
  %lv2 = load i16, ptr %A, align 1
  br label %if.end

if.end:                                           ; preds = %if.then, %for.body
  %c.sink = phi ptr [ %B, %if.then ], [ %C, %for.body ]
  %lv3 = load i16, ptr %c.sink
  %add = add i16 %lv3, 10
  store i16 %add, ptr %c.sink, align 1
  %iv.next = add nuw nsw i16 %iv, 1
  %tobool.not = icmp eq i16 %iv.next, 1000
  br i1 %tobool.not, label %for.end.loopexit, label %for.body

for.end.loopexit:                                 ; preds = %if.end
  ret void
}
