Skip to content

[flang][OpenACC] Reject valued gang/worker/vector outside kernels - #218695

Merged
delaram-talaashrafi merged 5 commits into
llvm:mainfrom
delaram-talaashrafi:vectorLenSemantic
Aug 25, 2026
Merged

[flang][OpenACC] Reject valued gang/worker/vector outside kernels#218695
delaram-talaashrafi merged 5 commits into
llvm:mainfrom
delaram-talaashrafi:vectorLenSemantic

Conversation

@delaram-talaashrafi

Copy link
Copy Markdown
Contributor

gang(num:), worker(n), and vector(n) are only valid on a loop associated with kernels, and then only if that kernels construct does not already specify num_gangs, num_workers, or vector_length.

`gang(num:)`, `worker(n)`, and `vector(n)` are only valid on a loop associated
with kernels, and then only if that kernels construct does not already
specify `num_gangs`, `num_workers`, or `vector_length`.
@llvmorg-github-actions llvmorg-github-actions Bot added flang Flang issues not falling into any other category flang:fir-hlfir openacc labels Aug 25, 2026
@llvmorg-github-actions

llvmorg-github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

@llvm/pr-subscribers-flang-semantics
@llvm/pr-subscribers-flang-parser
@llvm/pr-subscribers-openacc

@llvm/pr-subscribers-flang-fir-hlfir

Author: Delaram Talaashrafi (delaram-talaashrafi)

Changes

gang(num:), worker(n), and vector(n) are only valid on a loop associated with kernels, and then only if that kernels construct does not already specify num_gangs, num_workers, or vector_length.


Patch is 30.64 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/218695.diff

5 Files Affected:

  • (modified) flang/lib/Lower/OpenACC.cpp (+104)
  • (added) flang/test/Lower/OpenACC/acc-loop-level-clause-value.f90 (+395)
  • (modified) flang/test/Lower/OpenACC/acc-loop.f90 (-71)
  • (modified) flang/test/Lower/OpenACC/acc-parallel-loop.f90 (-79)
  • (modified) flang/test/Lower/OpenACC/acc-serial-loop.f90 (-77)
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 2e4b36178748b..cb85b63ad327c 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -46,6 +46,7 @@
 #include "mlir/IR/MLIRContext.h"
 #include "mlir/Support/LLVM.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/Frontend/OpenACC/ACC.h.inc"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
@@ -2353,6 +2354,61 @@ getAccLoopDoStmtEval(Fortran::lower::pft::Evaluation &eval) {
   return nullptr;
 }
 
+/// Reject a valued gang/worker/vector clause on a loop that is not associated
+/// with a kernels construct.
+static void checkLoopLevelClauseValueInKernels(
+    fir::FirOpBuilder &builder, mlir::Operation *parentCompute,
+    mlir::Location loc,
+    std::optional<mlir::acc::CombinedConstructsType> combinedConstructs,
+    llvm::StringRef clauseName) {
+  llvm::acc::Directive parentDir = llvm::acc::Directive::ACCD_loop;
+  if (combinedConstructs == mlir::acc::CombinedConstructsType::ParallelLoop)
+    parentDir = llvm::acc::Directive::ACCD_parallel_loop;
+  else if (combinedConstructs == mlir::acc::CombinedConstructsType::SerialLoop)
+    parentDir = llvm::acc::Directive::ACCD_serial_loop;
+  else if (mlir::isa_and_present<mlir::acc::ParallelOp>(parentCompute))
+    parentDir = llvm::acc::Directive::ACCD_parallel_loop;
+  else if (mlir::isa_and_present<mlir::acc::SerialOp>(parentCompute))
+    parentDir = llvm::acc::Directive::ACCD_serial_loop;
+  else if (mlir::acc::isAccRoutine(builder.getFunction().getOperation()))
+    parentDir = llvm::acc::Directive::ACCD_routine;
+
+  llvm::StringRef notAllowed =
+      parentDir == llvm::acc::Directive::ACCD_routine
+          ? "' not allowed in subprogram compiled with "
+          : "' not allowed in ";
+  fir::emitFatalError(
+      loc,
+      llvm::Twine("'") + clauseName + "(value)" + notAllowed +
+          Fortran::parser::ToUpperCaseLetters(
+              llvm::acc::getOpenACCDirectiveName(parentDir).str()) +
+          " directive",
+      /*genCrashDiag=*/false);
+}
+
+/// Reject a valued gang/worker/vector clause when the enclosing kernels
+/// construct already specifies the corresponding size clause.
+static void checkLoopLevelClauseConflictsWithKernels(
+    mlir::Operation *parentCompute, mlir::Location loc,
+    std::optional<mlir::acc::CombinedConstructsType> combinedConstructs,
+    llvm::StringRef loopClauseName, llvm::StringRef kernelsClauseName,
+    llvm::function_ref<bool(mlir::acc::KernelsOp)> hasKernelsClause) {
+  auto kernelsOp =
+      mlir::dyn_cast_if_present<mlir::acc::KernelsOp>(parentCompute);
+  if (!kernelsOp || !hasKernelsClause(kernelsOp))
+    return;
+
+  llvm::StringRef dirName =
+      combinedConstructs == mlir::acc::CombinedConstructsType::KernelsLoop
+          ? "KERNELS LOOP"
+          : "KERNELS";
+  fir::emitFatalError(loc,
+                      llvm::Twine("'") + loopClauseName +
+                          "(value)' not allowed in " + dirName +
+                          " region that has a " + kernelsClauseName + " clause",
+                      /*genCrashDiag=*/false);
+}
+
 static mlir::acc::LoopOp createLoopOp(
     Fortran::lower::AbstractConverter &converter,
     mlir::Location currentLocation,
@@ -2384,12 +2440,40 @@ static mlir::acc::LoopOp createLoopOp(
   crtDeviceTypes.push_back(mlir::acc::DeviceTypeAttr::get(
       builder.getContext(), mlir::acc::DeviceType::None));
 
+  // A valued gang/worker/vector clause is only allowed when the loop is
+  // associated with a kernels construct, and then only when the kernels
+  // construct does not already specify the corresponding size clause.
+  mlir::Operation *parentCompute =
+      mlir::acc::getEnclosingComputeOp(*builder.getBlock()->getParent());
+  bool isKernels =
+      mlir::isa_and_present<mlir::acc::KernelsOp>(parentCompute) ||
+      combinedConstructs == mlir::acc::CombinedConstructsType::KernelsLoop;
+
   for (const Fortran::parser::AccClause &clause : accClauseList.v) {
     mlir::Location clauseLocation = converter.genLocation(clause.source);
     if (const auto *gangClause =
             std::get_if<Fortran::parser::AccClause::Gang>(&clause.u)) {
       if (gangClause->v) {
         const Fortran::parser::AccGangArgList &x = *gangClause->v;
+        // Only the num argument is restricted to kernels. The static and dim
+        // arguments are allowed on any loop.
+        bool hasNumArg =
+            llvm::any_of(x.v, [](const Fortran::parser::AccGangArg &gangArg) {
+              return std::holds_alternative<Fortran::parser::AccGangArg::Num>(
+                  gangArg.u);
+            });
+        if (hasNumArg) {
+          if (!isKernels)
+            checkLoopLevelClauseValueInKernels(builder, parentCompute,
+                                               clauseLocation,
+                                               combinedConstructs, "Gang");
+          else
+            checkLoopLevelClauseConflictsWithKernels(
+                parentCompute, clauseLocation, combinedConstructs, "Gang",
+                "NUM_GANGS", [](mlir::acc::KernelsOp op) {
+                  return !op.getNumGangs().empty();
+                });
+        }
         mlir::SmallVector<mlir::Value> gangValues;
         mlir::SmallVector<mlir::Attribute> gangArgs;
         for (const Fortran::parser::AccGangArg &gangArg : x.v) {
@@ -2438,6 +2522,16 @@ static mlir::acc::LoopOp createLoopOp(
     } else if (const auto *workerClause =
                    std::get_if<Fortran::parser::AccClause::Worker>(&clause.u)) {
       if (workerClause->v) {
+        if (!isKernels)
+          checkLoopLevelClauseValueInKernels(builder, parentCompute,
+                                             clauseLocation, combinedConstructs,
+                                             "Worker");
+        else
+          checkLoopLevelClauseConflictsWithKernels(
+              parentCompute, clauseLocation, combinedConstructs, "Worker",
+              "NUM_WORKERS", [](mlir::acc::KernelsOp op) {
+                return !op.getNumWorkers().empty();
+              });
         mlir::Value workerNumValue = fir::getBase(converter.genExprValue(
             *Fortran::semantics::GetExpr(*workerClause->v), stmtCtx));
         for (auto crtDeviceTypeAttr : crtDeviceTypes) {
@@ -2451,6 +2545,16 @@ static mlir::acc::LoopOp createLoopOp(
     } else if (const auto *vectorClause =
                    std::get_if<Fortran::parser::AccClause::Vector>(&clause.u)) {
       if (vectorClause->v) {
+        if (!isKernels)
+          checkLoopLevelClauseValueInKernels(builder, parentCompute,
+                                             clauseLocation, combinedConstructs,
+                                             "Vector");
+        else
+          checkLoopLevelClauseConflictsWithKernels(
+              parentCompute, clauseLocation, combinedConstructs, "Vector",
+              "VECTOR_LENGTH", [](mlir::acc::KernelsOp op) {
+                return !op.getVectorLength().empty();
+              });
         mlir::Value vectorValue = fir::getBase(converter.genExprValue(
             *Fortran::semantics::GetExpr(*vectorClause->v), stmtCtx));
         for (auto crtDeviceTypeAttr : crtDeviceTypes) {
diff --git a/flang/test/Lower/OpenACC/acc-loop-level-clause-value.f90 b/flang/test/Lower/OpenACC/acc-loop-level-clause-value.f90
new file mode 100644
index 0000000000000..2c817e1ab308b
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-loop-level-clause-value.f90
@@ -0,0 +1,395 @@
+! Valued gang/worker/vector on a loop are only allowed when the loop is
+! associated with kernels, and then only when that kernels construct does not
+! already specify the matching size clause.
+
+! RUN: split-file %s %t
+
+! Non-kernels: standalone loop
+! RUN: not bbc -fopenacc -emit-hlfir %t/loop-gang.f90 -o - 2>&1 | FileCheck %s --check-prefix=LOOP-GANG
+! RUN: not bbc -fopenacc -emit-hlfir %t/loop-worker.f90 -o - 2>&1 | FileCheck %s --check-prefix=LOOP-WORKER
+! RUN: not bbc -fopenacc -emit-hlfir %t/loop-vector.f90 -o - 2>&1 | FileCheck %s --check-prefix=LOOP-VECTOR
+
+! Non-kernels: combined constructs
+! RUN: not bbc -fopenacc -emit-hlfir %t/parallel-loop-gang.f90 -o - 2>&1 | FileCheck %s --check-prefix=PLOOP-GANG
+! RUN: not bbc -fopenacc -emit-hlfir %t/parallel-loop-worker.f90 -o - 2>&1 | FileCheck %s --check-prefix=PLOOP-WORKER
+! RUN: not bbc -fopenacc -emit-hlfir %t/parallel-loop-vector.f90 -o - 2>&1 | FileCheck %s --check-prefix=PLOOP-VECTOR
+! RUN: not bbc -fopenacc -emit-hlfir %t/serial-loop-gang.f90 -o - 2>&1 | FileCheck %s --check-prefix=SLOOP-GANG
+! RUN: not bbc -fopenacc -emit-hlfir %t/serial-loop-worker.f90 -o - 2>&1 | FileCheck %s --check-prefix=SLOOP-WORKER
+! RUN: not bbc -fopenacc -emit-hlfir %t/serial-loop-vector.f90 -o - 2>&1 | FileCheck %s --check-prefix=SLOOP-VECTOR
+
+! Non-kernels: nested loop inside a compute construct
+! RUN: not bbc -fopenacc -emit-hlfir %t/parallel-nested-gang.f90 -o - 2>&1 | FileCheck %s --check-prefix=PAR-GANG
+! RUN: not bbc -fopenacc -emit-hlfir %t/parallel-nested-worker.f90 -o - 2>&1 | FileCheck %s --check-prefix=PAR-WORKER
+! RUN: not bbc -fopenacc -emit-hlfir %t/parallel-nested-vector.f90 -o - 2>&1 | FileCheck %s --check-prefix=PAR-VECTOR
+! RUN: not bbc -fopenacc -emit-hlfir %t/serial-nested-gang.f90 -o - 2>&1 | FileCheck %s --check-prefix=SER-GANG
+! RUN: not bbc -fopenacc -emit-hlfir %t/serial-nested-worker.f90 -o - 2>&1 | FileCheck %s --check-prefix=SER-WORKER
+! RUN: not bbc -fopenacc -emit-hlfir %t/serial-nested-vector.f90 -o - 2>&1 | FileCheck %s --check-prefix=SER-VECTOR
+
+! Non-kernels: orphan loop in an acc routine
+! RUN: not bbc -fopenacc -emit-hlfir %t/routine-gang.f90 -o - 2>&1 | FileCheck %s --check-prefix=RTN-GANG
+! RUN: not bbc -fopenacc -emit-hlfir %t/routine-worker.f90 -o - 2>&1 | FileCheck %s --check-prefix=RTN-WORKER
+! RUN: not bbc -fopenacc -emit-hlfir %t/routine-vector.f90 -o - 2>&1 | FileCheck %s --check-prefix=RTN-VECTOR
+
+! Kernels with a conflicting size clause
+! RUN: not bbc -fopenacc -emit-hlfir %t/kernels-num-gangs.f90 -o - 2>&1 | FileCheck %s --check-prefix=K-NG
+! RUN: not bbc -fopenacc -emit-hlfir %t/kernels-num-workers.f90 -o - 2>&1 | FileCheck %s --check-prefix=K-NW
+! RUN: not bbc -fopenacc -emit-hlfir %t/kernels-vector-length.f90 -o - 2>&1 | FileCheck %s --check-prefix=K-VL
+! RUN: not bbc -fopenacc -emit-hlfir %t/kernels-loop-num-gangs.f90 -o - 2>&1 | FileCheck %s --check-prefix=KL-NG
+! RUN: not bbc -fopenacc -emit-hlfir %t/kernels-loop-num-workers.f90 -o - 2>&1 | FileCheck %s --check-prefix=KL-NW
+! RUN: not bbc -fopenacc -emit-hlfir %t/kernels-loop-vector-length.f90 -o - 2>&1 | FileCheck %s --check-prefix=KL-VL
+
+! Allowed: kernels without a conflicting size clause, and bare clauses
+! RUN: bbc -fopenacc -emit-hlfir %t/allowed.f90 -o - | FileCheck %s --check-prefix=OK
+
+! LOOP-GANG: 'Gang(value)' not allowed in LOOP directive
+! LOOP-WORKER: 'Worker(value)' not allowed in LOOP directive
+! LOOP-VECTOR: 'Vector(value)' not allowed in LOOP directive
+! PLOOP-GANG: 'Gang(value)' not allowed in PARALLEL LOOP directive
+! PLOOP-WORKER: 'Worker(value)' not allowed in PARALLEL LOOP directive
+! PLOOP-VECTOR: 'Vector(value)' not allowed in PARALLEL LOOP directive
+! SLOOP-GANG: 'Gang(value)' not allowed in SERIAL LOOP directive
+! SLOOP-WORKER: 'Worker(value)' not allowed in SERIAL LOOP directive
+! SLOOP-VECTOR: 'Vector(value)' not allowed in SERIAL LOOP directive
+! PAR-GANG: 'Gang(value)' not allowed in PARALLEL LOOP directive
+! PAR-WORKER: 'Worker(value)' not allowed in PARALLEL LOOP directive
+! PAR-VECTOR: 'Vector(value)' not allowed in PARALLEL LOOP directive
+! SER-GANG: 'Gang(value)' not allowed in SERIAL LOOP directive
+! SER-WORKER: 'Worker(value)' not allowed in SERIAL LOOP directive
+! SER-VECTOR: 'Vector(value)' not allowed in SERIAL LOOP directive
+! RTN-GANG: 'Gang(value)' not allowed in subprogram compiled with ROUTINE directive
+! RTN-WORKER: 'Worker(value)' not allowed in subprogram compiled with ROUTINE directive
+! RTN-VECTOR: 'Vector(value)' not allowed in subprogram compiled with ROUTINE directive
+! K-NG: 'Gang(value)' not allowed in KERNELS region that has a NUM_GANGS clause
+! K-NW: 'Worker(value)' not allowed in KERNELS region that has a NUM_WORKERS clause
+! K-VL: 'Vector(value)' not allowed in KERNELS region that has a VECTOR_LENGTH clause
+! KL-NG: 'Gang(value)' not allowed in KERNELS LOOP region that has a NUM_GANGS clause
+! KL-NW: 'Worker(value)' not allowed in KERNELS LOOP region that has a NUM_WORKERS clause
+! KL-VL: 'Vector(value)' not allowed in KERNELS LOOP region that has a VECTOR_LENGTH clause
+
+!--- loop-gang.f90
+subroutine loop_gang(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc loop gang(8)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- loop-worker.f90
+subroutine loop_worker(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc loop worker(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- loop-vector.f90
+subroutine loop_vector(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc loop vector(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- parallel-loop-gang.f90
+subroutine parallel_loop_gang(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc parallel loop gang(8)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- parallel-loop-worker.f90
+subroutine parallel_loop_worker(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc parallel loop worker(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- parallel-loop-vector.f90
+subroutine parallel_loop_vector(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc parallel loop vector(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- serial-loop-gang.f90
+subroutine serial_loop_gang(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc serial loop gang(8)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- serial-loop-worker.f90
+subroutine serial_loop_worker(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc serial loop worker(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- serial-loop-vector.f90
+subroutine serial_loop_vector(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc serial loop vector(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- parallel-nested-gang.f90
+subroutine parallel_nested_gang(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc parallel
+  !$acc loop gang(8)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end parallel
+end subroutine
+
+!--- parallel-nested-worker.f90
+subroutine parallel_nested_worker(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc parallel
+  !$acc loop worker(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end parallel
+end subroutine
+
+!--- parallel-nested-vector.f90
+subroutine parallel_nested_vector(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc parallel
+  !$acc loop vector(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end parallel
+end subroutine
+
+!--- serial-nested-gang.f90
+subroutine serial_nested_gang(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc serial
+  !$acc loop gang(8)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end serial
+end subroutine
+
+!--- serial-nested-worker.f90
+subroutine serial_nested_worker(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc serial
+  !$acc loop worker(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end serial
+end subroutine
+
+!--- serial-nested-vector.f90
+subroutine serial_nested_vector(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc serial
+  !$acc loop vector(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end serial
+end subroutine
+
+!--- routine-gang.f90
+subroutine routine_gang(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc routine
+  !$acc loop gang(8)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- routine-worker.f90
+subroutine routine_worker(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc routine
+  !$acc loop worker(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- routine-vector.f90
+subroutine routine_vector(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc routine
+  !$acc loop vector(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- kernels-num-gangs.f90
+subroutine kernels_num_gangs(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc kernels num_gangs(8)
+  !$acc loop gang(8)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end kernels
+end subroutine
+
+!--- kernels-num-workers.f90
+subroutine kernels_num_workers(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc kernels num_workers(128)
+  !$acc loop worker(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end kernels
+end subroutine
+
+!--- kernels-vector-length.f90
+subroutine kernels_vector_length(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc kernels vector_length(128)
+  !$acc loop vector(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end kernels
+end subroutine
+
+!--- kernels-loop-num-gangs.f90
+subroutine kernels_loop_num_gangs(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc kernels loop num_gangs(8) gang(8)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- kernels-loop-num-workers.f90
+subroutine kernels_loop_num_workers(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc kernels loop num_workers(128) worker(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- kernels-loop-vector-length.f90
+subroutine kernels_loop_vector_length(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc kernels loop vector_length(128) vector(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+
+!--- allowed.f90
+subroutine allowed_kernels_valued(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc kernels
+  !$acc loop gang(8)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc loop worker(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc loop vector(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end kernels
+end subroutine
+! OK-LABEL: func.func @_QPallowed_kernels_valued
+! OK: acc.kernels
+! OK: acc.loop {{.*}}gang({num=%{{.*}} : i32})
+! OK: acc.loop {{.*}}worker(%{{.*}} : i32)
+! OK: acc.loop {{.*}}vector(%{{.*}} : i32)
+
+subroutine allowed_kernels_loop_valued(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc kernels loop gang(8)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc kernels loop worker(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc kernels loop vector(128)
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+end subroutine
+! OK-LABEL: func.func @_QPallowed_kernels_loop_valued
+! OK: acc.kernels {{.*}}combined
+! OK: acc.loop {{.*}}gang({num=%{{.*}} : i32})
+! OK: acc.loop {{.*}}worker(%{{.*}} : i32)
+! OK: acc.loop {{.*}}vector(%{{.*}} : i32)
+
+subroutine allowed_bare_and_size_clause(a, n)
+  integer :: i, n
+  real :: a(n)
+  !$acc parallel loop gang worker vector
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc kernels num_gangs(8) num_workers(128) vector_length(128)
+  !$acc loop gang worker vector
+  do i = 1, n
+    a(i) = a(i) + 1
+  end do
+  !$acc end kernels
+end subroutine
+! OK-LABEL: func.func @_QPallowed_bare_and_size_clause
+! OK: acc.parallel
+! OK: acc.loop {{.*}}gang
+! OK: acc.kernels {{.*}}num_gangs
+! OK: acc.loop {{.*}}gang
diff --git a/flang/test/Lower/OpenACC/acc-loop.f90 b/flang/test/Lower/OpenACC/acc-loop.f90
index 2a00f55b08751..3978f29dbf1fc 100644
--- a/flang/test/Lower/OpenACC/acc-loop.f90
+++ b/flang/test/Lower/OpenACC/acc-loop.f90
@@ -70,38 +70,6 @@ program acc_loop
 ! CHECK:      %[[PRIVATE_I:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) implicit(true) name("i") -> !fir.ref<i32>
 ! CHECK:      acc.loop gang private(%[[PRIVATE_I]] : !fir.ref<i32>) control(%arg0 : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
 ! CHECK:        acc.yield
-! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
-
-  !$acc loop gang(num: 8)
-  DO i = 1, n
-    a(i) = b(i)
-  END DO
-
-! CHECK:      [[GANGNUM1:%.*]] = arith.constant 8 : i32
-! CHECK:      %[[PRIVATE_I:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) implicit(true) name("i") -> !fir.ref<i32>
-! CHECK:      acc.loop gang({num=[[GANGNUM1]] : i32}) private(%[[PRIVATE_I...
[truncated]

@clementval clementval left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be better to be done in semantic checking in check-acc-structure.

@@ -1030,6 +1128,9 @@ void AccStructureChecker::Enter(const parser::AccClause::Vector &g) {
if (GetContext().directive != llvm::acc::Directive::ACCD_routine) {
CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type);
}
if (g.v) {
CheckLoopLevelClauseValue("Vector");

@clementval clementval Aug 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semantic message always makes the clause or directive in full uppercase.

You can use parser::ToUpperCaseLetters(getClauseName([clause](llvm::acc::Clause::ACCC_vector)).str()

Same for the worker and gang

@clementval clementval left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for addressing the comments.

@delaram-talaashrafi
delaram-talaashrafi merged commit 0905c44 into llvm:main Aug 25, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

flang:fir-hlfir flang:parser flang:semantics flang Flang issues not falling into any other category openacc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants