! Test forall scheduling analysis from lower-hlfir-ordered-assignments pass.
! The printed output is done via LLVM_DEBUG, hence the "asserts" requirement.
! This test test that conflicting actions are not scheduled to be evaluated
! in the same loops (same run id).

! RUN: bbc -hlfir -o - -pass-pipeline="builtin.module(lower-hlfir-ordered-assignments)" --debug-only=flang-ordered-assignment -flang-dbg-order-assignment-schedule-only %s 2>&1 | FileCheck %s
! REQUIRES: asserts

subroutine no_conflict(x)
  real :: x(:)
  forall(i=1:10) x(i) = i
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPno_conflict ------------
!CHECK-NEXT: run 1 evaluate: forall/region_assign1

subroutine rhs_lhs_overlap(x)
  real :: x(:)
  forall(i=1:10) x(i) = x(11-i)
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPrhs_lhs_overlap ------------
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?xf32>>' at index: 0 W:<block argument> of type '!fir.box<!fir.array<?xf32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/region_assign1/rhs
!CHECK-NEXT: run 2 evaluate: forall/region_assign1

subroutine no_rhs_lhs_overlap(x, y)
  real :: x(:), y(:)
  forall(i=1:10) x(i) = y(i)
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPno_rhs_lhs_overlap ------------
!CHECK-NEXT: run 1 evaluate: forall/region_assign1

subroutine no_rhs_lhs_overlap_2(x)
  real :: x(:), y(10)
  forall(i=1:10) x(i) = y(i)
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPno_rhs_lhs_overlap_2 ------------
!CHECK-NEXT: run 1 evaluate: forall/region_assign1

subroutine no_rhs_lhs_overlap_3()
  real :: x(10), y(10)
  forall(i=1:10) x(i) = y(i)
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPno_rhs_lhs_overlap_3 ------------
!CHECK-NEXT: run 1 evaluate: forall/region_assign1

subroutine array_expr_rhs_lhs_overlap(x)
  real :: x(:, :)
  forall(i=1:10) x(i, :) = x(:, i)*2
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QParray_expr_rhs_lhs_overlap ------------
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?x?xf32>>' at index: 0 W:<block argument> of type '!fir.box<!fir.array<?x?xf32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/region_assign1/rhs
!CHECK-NEXT: run 2 evaluate: forall/region_assign1

subroutine array_expr_no_rhs_lhs_overlap(x, y, z)
  real :: x(:, :), y(:, :), z(:, :)
  forall(i=1:10) x(i, :) = y(:, i) + z(i, :)
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QParray_expr_no_rhs_lhs_overlap ------------
!CHECK-NEXT: run 1 evaluate: forall/region_assign1

subroutine rhs_lhs_overlap_2(x, y)
  real, target :: x(:), y(:)
  forall(i=1:10) x(i) = y(i)
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPrhs_lhs_overlap_2 ------------
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?xf32>>' at index: 1 W:<block argument> of type '!fir.box<!fir.array<?xf32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/region_assign1/rhs
!CHECK-NEXT: run 2 evaluate: forall/region_assign1

subroutine lhs_lhs_overlap(x)
  integer :: x(10)
  forall(i=1:10) x(x(i)) = i
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPlhs_lhs_overlap ------------
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.ref<!fir.array<10xi32>>' at index: 0 W:<block argument> of type '!fir.ref<!fir.array<10xi32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/region_assign1/lhs
!CHECK-NEXT: run 2 evaluate: forall/region_assign1

subroutine unknown_function_call(x)
  interface
    pure real function foo(x, i)
      integer, intent(in) :: i
      real, intent(in) :: x(10)
    end function
  end interface
  real :: x(10)
  forall(i=1:10) x(i) = foo(x, i)
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPunknown_function_call ------------
!CHECK-NEXT: unknown effect: {{.*}} fir.call @_QPfoo
!CHECK-NEXT: unknown effect: {{.*}} fir.call @_QPfoo
!CHECK-NEXT: conflict: R/W: <unknown> W:<block argument> of type '!fir.ref<!fir.array<10xf32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/region_assign1/rhs
!CHECK-NEXT: run 2 evaluate: forall/region_assign1

subroutine unknown_function_call2(x)
  interface
    pure real function foo2(i)
      integer, value :: i
    end function
  end interface
  ! foo2 may read x since it is a target, even if it is pure,
  ! if the actual argument of x is a module variable accessible
  ! to foo via host association.
  real, target :: x(:)
  forall(i=1:10) x(i) = foo2(i)
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPunknown_function_call2 ------------
!CHECK-NEXT: unknown effect: {{.*}} fir.call @_QPfoo2(
!CHECK-NEXT: unknown effect: {{.*}} fir.call @_QPfoo2(
!CHECK-NEXT: conflict: R/W: <unknown> W:<block argument> of type '!fir.box<!fir.array<?xf32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/region_assign1/rhs
!CHECK-NEXT: run 2 evaluate: forall/region_assign1

subroutine forall_mask_conflict(x)
  integer :: x(:)
  forall(i=1:10, x(11-i)>0) x(i) = 42
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPforall_mask_conflict ------------
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?xi32>>' at index: 0 W:<block argument> of type '!fir.box<!fir.array<?xi32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/forall_mask1/mask
!CHECK-NEXT: run 2 evaluate: forall/forall_mask1/region_assign1

subroutine forall_ub_conflict(x, y)
  integer :: x(:, :)
  forall(i=1:10)
    forall(j=1:x(i,i))
      x(i, j) = 42
    end forall
  end forall
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPforall_ub_conflict ------------
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 0 W:<block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/forall1/ub
!CHECK-NEXT: run 2 evaluate: forall/forall1/region_assign1

subroutine sequential_assign(x, y)
  integer :: x(:), y(:)
  forall(i=1:10)
    x(i) = y(i)
    y(2*i) = x(i)
  end forall
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPsequential_assign ------------
!CHECK-NEXT: run 1 evaluate: forall/region_assign1
!CHECK-NEXT: run 2 evaluate: forall/region_assign2

subroutine loads_of_conlficts(x, y)
  integer, target :: x(:, :), y(:, :)
  forall(i=1:10)
    forall (j=1:y(i,i)) x(x(i, j), j) = y(i, j)
    forall (j=1:x(i,i), y(i,i)>0) y(x(i, j), j) = 0
  end forall
end subroutine
!CHECK-LABEL: ------------ scheduling forall in _QPloads_of_conlficts ------------
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 1 W:<block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/forall1/ub
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 1 W:<block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/forall1/region_assign1/rhs
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 0 W:<block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 0
!CHECK-NEXT: run 1 save    : forall/forall1/region_assign1/lhs
!CHECK-NEXT: run 2 evaluate: forall/forall1/region_assign1
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 0 W:<block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 1
!CHECK-NEXT: run 3 save    : forall/forall2/ub
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 1 W:<block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 1
!CHECK-NEXT: run 3 save    : forall/forall2/forall_mask1/mask
!CHECK-NEXT: conflict: R/W: <block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 0 W:<block argument> of type '!fir.box<!fir.array<?x?xi32>>' at index: 1
!CHECK-NEXT: run 3 save    : forall/forall2/forall_mask1/region_assign1/lhs
!CHECK-NEXT: run 4 evaluate: forall/forall2/forall_mask1/region_assign1
