// Tests multiple kernels running concurrently. Runs two kernels, which
// increment a global atomic counter and wait for the counter to reach 2.
//
// RUN: mlir-opt %s \
// RUN: | mlir-opt -gpu-lower-to-nvvm-pipeline="cubin-format=%gpu_compilation_format allow-pattern-rollback=0" \
// RUN: | env CUDA_MODULE_LOADING=EAGER mlir-runner \
// RUN:   --shared-libs=%mlir_cuda_runtime \
// RUN:   --shared-libs=%mlir_runner_utils \
// RUN:   --entry-point-result=void

// CUDA_MODULE_LOADING=EAGER avoids an implicit context synchronization on first
// use of each kernel. It is technically not needed for this test, because
// there is only one kernel.

module attributes {gpu.container_module} {

gpu.module @kernels {
  gpu.func @kernel(%memref: memref<i32>) kernel {
    %c0 = arith.constant 0 : i32
    %c1 = arith.constant 1 : i32
    %c2 = arith.constant 2 : i32
    %block = memref.atomic_rmw addi %c1, %memref[] : (i32, memref<i32>) -> i32
    scf.while: () -> () {
      %value = memref.atomic_rmw addi %c0, %memref[] : (i32, memref<i32>) -> i32
      %cond = arith.cmpi slt, %value, %c2 : i32
      scf.condition(%cond)
    } do {
      scf.yield
    }
    gpu.return
  }
}

func.func @main() {
  %c0 = arith.constant 0 : i32
  %c1 = arith.constant 1 : index
  %memref = gpu.alloc host_shared () : memref<i32>
  memref.store %c0, %memref[] : memref<i32>
  %0 = gpu.wait async
  %1 = gpu.wait async
  %2 = gpu.launch_func async [%0] @kernels::@kernel
      blocks in (%c1, %c1, %c1)
      threads in (%c1, %c1, %c1)
      args(%memref: memref<i32>)
  %3 = gpu.launch_func async [%1] @kernels::@kernel
      blocks in (%c1, %c1, %c1)
      threads in (%c1, %c1, %c1)
      args(%memref: memref<i32>)
  gpu.wait [%2, %3]
  return
}

}