! The "thread_limit" clause was added to the "target" construct in OpenMP 5.1.
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=51 %s -o - | FileCheck %s --check-prefixes=BOTH,HOST
! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=51 -fopenmp-is-target-device %s -o - | FileCheck %s --check-prefixes=BOTH,DEVICE

! BOTH-LABEL: func.func @_QPteams
subroutine teams()
  ! BOTH: omp.target

  ! HOST-SAME: host_eval(%{{.*}} -> %[[NUM_TEAMS:.*]], %{{.*}} -> %[[THREAD_LIMIT:.*]] : i32, i32)
  
  ! DEVICE-NOT: host_eval({{.*}})
  ! DEVICE-SAME: {
  !$omp target

  ! BOTH: omp.teams

  ! HOST-SAME: num_teams( to %[[NUM_TEAMS]] : i32) thread_limit(%[[THREAD_LIMIT]] : i32)
  ! DEVICE-SAME: num_teams({{.*}}) thread_limit({{.*}})
  !$omp teams num_teams(1) thread_limit(2)
  call foo()
  !$omp end teams

  !$omp end target

  ! BOTH: omp.teams
  ! BOTH-SAME: num_teams({{.*}}) thread_limit({{.*}}) {
  !$omp teams num_teams(1) thread_limit(2)
  call foo()
  !$omp end teams
end subroutine teams

! BOTH-LABEL: func.func @_QPdistribute_parallel_do
subroutine distribute_parallel_do()
  ! BOTH: omp.target
  
  ! HOST-SAME: host_eval(%{{.*}} -> %[[LB:.*]], %{{.*}} -> %[[UB:.*]], %{{.*}} -> %[[STEP:.*]], %{{.*}} -> %[[NUM_THREADS:.*]] : i32, i32, i32, i32)
  
  ! DEVICE-NOT: host_eval({{.*}})
  ! DEVICE-SAME: {

  ! BOTH: omp.teams
  !$omp target teams

  ! BOTH: omp.parallel

  ! HOST-SAME: num_threads(%[[NUM_THREADS]] : i32)
  ! DEVICE-SAME: num_threads({{.*}})

  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.wsloop
  ! BOTH-NEXT: omp.loop_nest

  ! HOST-SAME: (%{{.*}}) : i32 = (%[[LB]]) to (%[[UB]]) inclusive step (%[[STEP]])
  !$omp distribute parallel do num_threads(1)
  do i=1,10
    call foo()
  end do
  !$omp end distribute parallel do
  !$omp end target teams

  ! BOTH: omp.target
  ! BOTH-NOT: host_eval({{.*}})
  ! BOTH-SAME: {
  ! BOTH: omp.teams
  !$omp target teams
  call foo() !< Prevents this from being SPMD.

  ! BOTH: omp.parallel
  ! BOTH-SAME: num_threads({{.*}})
  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.wsloop
  !$omp distribute parallel do num_threads(1)
  do i=1,10
    call foo()
  end do
  !$omp end distribute parallel do
  !$omp end target teams

  ! BOTH: omp.teams
  !$omp teams

  ! BOTH: omp.parallel
  ! BOTH-SAME: num_threads({{.*}})
  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.wsloop
  !$omp distribute parallel do num_threads(1)
  do i=1,10
    call foo()
  end do
  !$omp end distribute parallel do
  !$omp end teams
end subroutine distribute_parallel_do

! BOTH-LABEL: func.func @_QPdistribute_parallel_do_simd
subroutine distribute_parallel_do_simd()
  ! BOTH: omp.target
  
  ! HOST-SAME: host_eval(%{{.*}} -> %[[LB:.*]], %{{.*}} -> %[[UB:.*]], %{{.*}} -> %[[STEP:.*]], %{{.*}} -> %[[NUM_THREADS:.*]] : i32, i32, i32, i32)
  
  ! DEVICE-NOT: host_eval({{.*}})
  ! DEVICE-SAME: {

  ! BOTH: omp.teams
  !$omp target teams

  ! BOTH: omp.parallel

  ! HOST-SAME: num_threads(%[[NUM_THREADS]] : i32)
  ! DEVICE-SAME: num_threads({{.*}})

  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.wsloop
  ! BOTH-NEXT: omp.simd
  ! BOTH-NEXT: omp.loop_nest

  ! HOST-SAME: (%{{.*}}) : i32 = (%[[LB]]) to (%[[UB]]) inclusive step (%[[STEP]])
  !$omp distribute parallel do simd num_threads(1)
  do i=1,10
    call foo()
  end do
  !$omp end distribute parallel do simd
  !$omp end target teams

  ! BOTH: omp.target
  ! BOTH-NOT: host_eval({{.*}})
  ! BOTH-SAME: {
  ! BOTH: omp.teams
  !$omp target teams
  call foo() !< Prevents this from being SPMD.

  ! BOTH: omp.parallel
  ! BOTH-SAME: num_threads({{.*}})
  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.wsloop
  ! BOTH-NEXT: omp.simd
  !$omp distribute parallel do simd num_threads(1)
  do i=1,10
    call foo()
  end do
  !$omp end distribute parallel do simd
  !$omp end target teams

  ! BOTH: omp.teams
  !$omp teams

  ! BOTH: omp.parallel
  ! BOTH-SAME: num_threads({{.*}})
  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.wsloop
  ! BOTH-NEXT: omp.simd
  !$omp distribute parallel do simd num_threads(1)
  do i=1,10
    call foo()
  end do
  !$omp end distribute parallel do simd
  !$omp end teams
end subroutine distribute_parallel_do_simd

! BOTH-LABEL: func.func @_QPdistribute
subroutine distribute()
  ! BOTH: omp.target
  
  ! HOST-SAME: host_eval(%{{.*}} -> %[[LB:.*]], %{{.*}} -> %[[UB:.*]], %{{.*}} -> %[[STEP:.*]] : i32, i32, i32)
  
  ! DEVICE-NOT: host_eval({{.*}})
  ! DEVICE-SAME: {

  ! BOTH: omp.teams
  !$omp target teams

  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.loop_nest

  ! HOST-SAME: (%{{.*}}) : i32 = (%[[LB]]) to (%[[UB]]) inclusive step (%[[STEP]])
  !$omp distribute
  do i=1,10
    call foo()
  end do
  !$omp end distribute
  !$omp end target teams

  ! BOTH: omp.target
  ! BOTH-NOT: host_eval({{.*}})
  ! BOTH-SAME: {
  ! BOTH: omp.teams
  !$omp target teams
  call foo() !< Prevents this from being Generic-SPMD.

  ! BOTH: omp.distribute
  !$omp distribute
  do i=1,10
    call foo()
  end do
  !$omp end distribute
  !$omp end target teams

  ! BOTH: omp.teams
  !$omp teams

  ! BOTH: omp.distribute
  !$omp distribute
  do i=1,10
    call foo()
  end do
  !$omp end distribute
  !$omp end teams
end subroutine distribute

! BOTH-LABEL: func.func @_QPdistribute_simd
subroutine distribute_simd()
  ! BOTH: omp.target
  
  ! HOST-SAME: host_eval(%{{.*}} -> %[[LB:.*]], %{{.*}} -> %[[UB:.*]], %{{.*}} -> %[[STEP:.*]] : i32, i32, i32)
  
  ! DEVICE-NOT: host_eval({{.*}})
  ! DEVICE-SAME: {

  ! BOTH: omp.teams
  !$omp target teams

  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.simd
  ! BOTH-NEXT: omp.loop_nest

  ! HOST-SAME: (%{{.*}}) : i32 = (%[[LB]]) to (%[[UB]]) inclusive step (%[[STEP]])
  !$omp distribute simd
  do i=1,10
    call foo()
  end do
  !$omp end distribute simd
  !$omp end target teams

  ! BOTH: omp.target
  ! BOTH-NOT: host_eval({{.*}})
  ! BOTH-SAME: {
  ! BOTH: omp.teams
  !$omp target teams
  call foo() !< Prevents this from being Generic-SPMD.

  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.simd
  !$omp distribute simd
  do i=1,10
    call foo()
  end do
  !$omp end distribute simd
  !$omp end target teams

  ! BOTH: omp.teams
  !$omp teams

  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.simd
  !$omp distribute simd
  do i=1,10
    call foo()
  end do
  !$omp end distribute simd
  !$omp end teams
end subroutine distribute_simd

! BOTH-LABEL: func.func @_QPloop
subroutine loop()
  ! BOTH: omp.target
  
  ! HOST-SAME: host_eval(%{{.*}} -> %[[LB:.*]], %{{.*}} -> %[[UB:.*]], %{{.*}} -> %[[STEP:.*]] : i32, i32, i32)
  
  ! DEVICE-NOT: host_eval({{.*}})
  ! DEVICE-SAME: {

  ! BOTH: omp.teams
  !$omp target teams

  ! BOTH: omp.parallel

  ! BOTH: omp.distribute
  ! BOTH-NEXT: omp.wsloop
  ! BOTH-NEXT: omp.loop_nest

  ! HOST-SAME: (%{{.*}}) : i32 = (%[[LB]]) to (%[[UB]]) inclusive step (%[[STEP]])
  !$omp loop
  do i=1,10
  end do
  !$omp end target teams
end subroutine loop
