[Mlir-commits] [flang] [mlir] [flang][mlir] Add checks and test for linear clause on omp.wsloop and omp.simd (PR #174916)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Jan 8 19:17:39 PST 2026
https://github.com/NimishMishra updated https://github.com/llvm/llvm-project/pull/174916
>From d8a13da7b7db2eacba4cbe2de44902f86c36656a Mon Sep 17 00:00:00 2001
From: NimishMishra <neelam.nimish at gmail.com>
Date: Thu, 8 Jan 2026 11:58:05 +0530
Subject: [PATCH 1/4] [flang][mlir] Add checks and tests for linear clause
---
flang/docs/OpenMPSupport.md | 12 +-
flang/lib/Lower/OpenMP/OpenMP.cpp | 96 +++++++++------
.../Lower/OpenMP/composite_simd_linear.f90 | 110 ++++++++++++++++++
.../OpenMP/distribute-parallel-do-simd.f90 | 10 +-
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 9 ++
.../Target/LLVMIR/openmp-llvm-invalid.mlir | 28 +++++
6 files changed, 218 insertions(+), 47 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/composite_simd_linear.f90
diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md
index 63f4dbea98180..74ef078fbf60f 100644
--- a/flang/docs/OpenMPSupport.md
+++ b/flang/docs/OpenMPSupport.md
@@ -36,29 +36,29 @@ Note : No distinction is made between the support in Parser/Semantics, MLIR, Low
| proc_bind clause | Y | |
| simd construct | P | Implicit linearization is skipped if iv is a pointer or allocatable|
| declare simd construct | N | |
-| do simd construct | P | linear clause is not supported |
+| do simd construct | P | Implicit linearization is skipped if iv is a pointer or allocatable |
| target data construct | Y | |
| target construct | Y | |
| target update construct | Y | |
| declare target directive | Y | |
| teams construct | Y | |
| distribute construct | Y | |
-| distribute simd construct | P | linear clauses are not supported |
+| distribute simd construct | P | Implicit linearization is skipped if iv is a pointer or allocatable |
| distribute parallel loop construct | Y | |
-| distribute parallel loop simd construct | P | linear clauses are not supported |
+| distribute parallel loop simd construct | P | Implicit linearization is skipped if iv is a pointer or allocatable |
| depend clause | Y | |
| declare reduction construct | N | |
| atomic construct extensions | Y | |
| cancel construct | Y | |
| cancellation point construct | Y | |
-| parallel do simd construct | P | linear clause not supported |
+| parallel do simd construct | P | Implicit linearization is skipped if iv is a pointer or allocatable |
| target teams construct | Y | |
| teams distribute construct | Y | |
-| teams distribute simd construct | P | linear clause is not supported |
+| teams distribute simd construct | P | Implicit linearization is skipped if iv is a pointer or allocatable |
| target teams distribute construct | Y | |
| teams distribute parallel loop construct | Y | |
| target teams distribute parallel loop construct | Y | |
-| teams distribute parallel loop simd construct | P | linear clause is not supported |
+| teams distribute parallel loop simd construct | P | Implicit linearization is skipped if iv is a pointer or allocatable |
| target teams distribute parallel loop simd construct | P | linear clause is not supported |
## Extensions
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 4381d1e9064cf..989e370870f33 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1639,6 +1639,62 @@ static void genSimdClauses(
cp.processLinear(clauseOps);
}
+// SIMD construct may have implicit
+// linear semantics on IV. Process the same here.
+static void
+genSimdImplicitLinear(lower::AbstractConverter &converter,
+ mlir::omp::SimdOperands &clauseOps,
+ mlir::omp::LoopNestOperands loopNestClauseOps,
+ llvm::SmallVector<const semantics::Symbol *> iv) {
+
+ // If the (standalone/composite) SIMD is enclosed within TARGET,
+ // implicit linearization will cause invalid FIR due to
+ // target operation `host_eval` argument's illegal use in omp.simd.
+ // Hence skip implicit linearization if TARGET encloses the current
+ // SIMD.
+ auto *currentOp =
+ converter.getFirOpBuilder().getInsertionBlock()->getParentOp();
+ while (currentOp) {
+ if (auto targetOp = mlir::dyn_cast<mlir::omp::TargetOp>(currentOp))
+ return;
+ currentOp = currentOp->getParentOp();
+ }
+
+ std::vector<mlir::Attribute> typeAttrs;
+ // If attributes from explicit `linear(...)` clause are present,
+ // carry them forward.
+ if (clauseOps.linearVarTypes && !clauseOps.linearVarTypes.empty())
+ typeAttrs.assign(clauseOps.linearVarTypes.begin(),
+ clauseOps.linearVarTypes.end());
+
+ for (auto [loopVar, loopStep] : llvm::zip(iv, loopNestClauseOps.loopSteps)) {
+ const mlir::Value variable = converter.getSymbolAddress(*loopVar);
+
+ // If the loop variable is already linearized (through an explicit
+ // `linear()` clause, skip.
+ if (std::find(clauseOps.linearVars.begin(), clauseOps.linearVars.end(),
+ variable) != clauseOps.linearVars.end())
+ continue;
+
+ // TODO: Implicit linearization is skipped if iv is a pointer
+ // or an allocatable, due to potential mismatch between the linear
+ // variable type (example !fir.ref<!fir.box<!fir.heap<i32>>>)
+ // and the linear step size (example: i64). Handle this type mismatch
+ // gracefully.
+ if (loopVar->test(Fortran::semantics::Symbol::Flag::OmpLinear) &&
+ !(Fortran::semantics::IsAllocatableOrPointer(*loopVar) ||
+ Fortran::semantics::IsAllocatableOrPointer(loopVar->GetUltimate()))) {
+ mlir::Type ty = converter.genType(*loopVar);
+ typeAttrs.push_back(mlir::TypeAttr::get(ty));
+ clauseOps.linearVars.push_back(variable);
+ clauseOps.linearStepVars.push_back(loopStep);
+ }
+ }
+ if (!typeAttrs.empty())
+ clauseOps.linearVarTypes =
+ mlir::ArrayAttr::get(&converter.getMLIRContext(), typeAttrs);
+}
+
static void genSingleClauses(lower::AbstractConverter &converter,
semantics::SemanticsContext &semaCtx,
const List<Clause> &clauses, mlir::Location loc,
@@ -3019,48 +3075,13 @@ genStandaloneSimd(lower::AbstractConverter &converter, lower::SymMap &symTable,
llvm::SmallVector<const semantics::Symbol *> iv;
genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc,
loopNestClauseOps, iv);
+ genSimdImplicitLinear(converter, simdClauseOps, loopNestClauseOps, iv);
EntryBlockArgs simdArgs;
simdArgs.priv.syms = dsp.getDelayedPrivSymbols();
simdArgs.priv.vars = simdClauseOps.privateVars;
simdArgs.reduction.syms = simdReductionSyms;
simdArgs.reduction.vars = simdClauseOps.reductionVars;
-
- std::vector<mlir::Attribute> typeAttrs;
- // If attributes from explicit `linear(...)` clause are present,
- // carry them forward.
- if (simdClauseOps.linearVarTypes && !simdClauseOps.linearVarTypes.empty())
- typeAttrs.assign(simdClauseOps.linearVarTypes.begin(),
- simdClauseOps.linearVarTypes.end());
-
- for (auto [loopVar, loopStep] : llvm::zip(iv, loopNestClauseOps.loopSteps)) {
- const mlir::Value variable = converter.getSymbolAddress(*loopVar);
-
- // If the loop variable is already linearized (through an explicit
- // `linear()` clause, skip.
- if (std::find(simdClauseOps.linearVars.begin(),
- simdClauseOps.linearVars.end(),
- variable) != simdClauseOps.linearVars.end())
- continue;
-
- // TODO: Implicit linearization is skipped if iv is a pointer
- // or an allocatable, due to potential mismatch between the linear
- // variable type (example !fir.ref<!fir.box<!fir.heap<i32>>>)
- // and the linear step size (example: i64). Handle this type mismatch
- // gracefully.
- if (loopVar->test(Fortran::semantics::Symbol::Flag::OmpLinear) &&
- !(Fortran::semantics::IsAllocatableOrPointer(*loopVar) ||
- Fortran::semantics::IsAllocatableOrPointer(loopVar->GetUltimate()))) {
- mlir::Type ty = converter.genType(*loopVar);
- typeAttrs.push_back(mlir::TypeAttr::get(ty));
- simdClauseOps.linearVars.push_back(variable);
- simdClauseOps.linearStepVars.push_back(loopStep);
- }
- }
- if (!typeAttrs.empty())
- simdClauseOps.linearVarTypes =
- mlir::ArrayAttr::get(&converter.getMLIRContext(), typeAttrs);
-
auto simdOp =
genWrapperOp<mlir::omp::SimdOp>(converter, loc, simdClauseOps, simdArgs);
genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, item,
@@ -3233,6 +3254,7 @@ static mlir::omp::DistributeOp genCompositeDistributeParallelDoSimd(
llvm::SmallVector<const semantics::Symbol *> iv;
genLoopNestClauses(converter, semaCtx, eval, simdItem->clauses, loc,
loopNestClauseOps, iv);
+ genSimdImplicitLinear(converter, simdClauseOps, loopNestClauseOps, iv);
// Operation creation.
EntryBlockArgs distributeArgs;
@@ -3304,6 +3326,7 @@ static mlir::omp::DistributeOp genCompositeDistributeSimd(
llvm::SmallVector<const semantics::Symbol *> iv;
genLoopNestClauses(converter, semaCtx, eval, simdItem->clauses, loc,
loopNestClauseOps, iv);
+ genSimdImplicitLinear(converter, simdClauseOps, loopNestClauseOps, iv);
// Operation creation.
EntryBlockArgs distributeArgs;
@@ -3366,6 +3389,7 @@ static mlir::omp::WsloopOp genCompositeDoSimd(
llvm::SmallVector<const semantics::Symbol *> iv;
genLoopNestClauses(converter, semaCtx, eval, simdItem->clauses, loc,
loopNestClauseOps, iv);
+ genSimdImplicitLinear(converter, simdClauseOps, loopNestClauseOps, iv);
// Operation creation.
EntryBlockArgs wsloopArgs;
diff --git a/flang/test/Lower/OpenMP/composite_simd_linear.f90 b/flang/test/Lower/OpenMP/composite_simd_linear.f90
new file mode 100644
index 0000000000000..c8ea0238c039b
--- /dev/null
+++ b/flang/test/Lower/OpenMP/composite_simd_linear.f90
@@ -0,0 +1,110 @@
+! RUN: %flang_fc1 -fopenmp -emit-hlfir %s -o - 2>&1 | FileCheck %s
+
+
+subroutine do_simd
+!CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFdo_simdEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFdo_simdEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[CONST:.*]] = arith.constant 1 : i32
+!CHECK: %{{.*}} = arith.constant 1 : i32
+!CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32
+!CHECK: omp.wsloop {
+!CHECK: omp.simd linear(%[[X]]#0 = %[[CONST]] : !fir.ref<i32>, %[[I]]#0 = %[[IV_STEP]] : !fir.ref<i32>) private(@_QFdo_simdEi_private_i32 {{.*}} -> %arg0 : !fir.ref<i32>) {
+!CHECK: }
+!CHECK: } {linear_var_types = [i32, i32], omp.composite}
+!CHECK: } {omp.composite}
+ integer :: x
+ !$omp do simd linear(x:1)
+ do i = 1, N
+ end do
+ !$omp end do simd
+end subroutine do_simd
+
+
+subroutine distribute_simd
+!CHECK: omp.teams {
+!CHECK: omp.distribute private(@_QFdistribute_simdEi_private_i32 {{.*}} -> %[[ARG0:.*]] : !fir.ref<i32>) {
+!CHECK: omp.simd linear(%[[ARG0]] = %c1_i32 : !fir.ref<i32>) private(@_QFdistribute_simdEi_private_i32 %[[ARG0]] -> {{.*}} : !fir.ref<i32>) {
+!CHECK: } {linear_var_types = [i32], omp.composite}
+!CHECK: } {omp.composite}
+ integer :: i
+ integer :: x
+ !$omp teams
+ !$omp distribute simd linear(i:1)
+ do i = 1, N
+ end do
+ !$omp end distribute simd
+ !$omp end teams
+end subroutine distribute_simd
+
+
+subroutine distribute_parallel_do
+!CHECK: %[[I:.*]]:2 = hlfir.declare {{.*}} {uniq_name = "_QFdistribute_parallel_doEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.teams {
+!CHECK: omp.parallel {
+!CHECK: %[[CONST]] = arith.constant 1 : i32
+!CHECK: omp.distribute {
+!CHECK: omp.wsloop {
+!CHECK: omp.simd linear(%[[I]]#0 = %[[CONST]] : !fir.ref<i32>) private(@_QFdistribute_parallel_doEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
+ !$omp teams
+ !$omp distribute parallel do simd linear(i:1)
+ do i = 1, N
+ end do
+ !$omp end distribute parallel do simd
+!CHECK: } {linear_var_types = [i32], omp.composite}
+ !$omp end teams
+end subroutine distribute_parallel_do
+
+subroutine parallel_do
+!CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFparallel_doEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFparallel_doEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.parallel {
+!CHECK: %[[LINEAR_STEP:.*]] = arith.constant 2 : i32
+!CHECK: %{{.*}} = arith.constant 1 : i32
+!CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32
+!CHECK: omp.wsloop {
+!CHECK: omp.simd linear(%[[X]]#0 = %[[LINEAR_STEP]] : !fir.ref<i32>, %[[I]]#0 = %[[IV_STEP]] : !fir.ref<i32>)
+!private(@_QFparallel_doEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
+ integer :: x
+ !$omp parallel do simd linear(x:2)
+ do i = 1, N
+ end do
+ !$omp end parallel do simd
+!CHECK: } {linear_var_types = [i32, i32], omp.composite}
+end subroutine parallel_do
+
+subroutine teams_distribute
+!CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFteams_distributeEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFteams_distributeEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.teams {
+!CHECK: %[[LINEAR_STEP:.*]] = arith.constant 1 : i32
+!CHECK: {{.*}} = arith.constant 1 : i32
+!CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32
+!CHECK: omp.distribute {
+!CHECK: omp.simd linear(%[[X]]#0 = %[[LINEAR_STEP]] : !fir.ref<i32>, %[[I]]#0 = %[[IV_STEP]] : !fir.ref<i32>)
+!private(@_QFteams_distributeEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
+ integer :: x
+ !$omp teams distribute simd linear(x)
+ do i = 1, N
+ end do
+ !$omp end teams distribute simd
+!CHECK: } {linear_var_types = [i32, i32], omp.composite}
+end subroutine teams_distribute
+
+subroutine teams_distribute_parallel_do
+!CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFteams_distribute_parallel_doEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFteams_distribute_parallel_doEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: omp.teams {
+!CHECK: omp.parallel {
+gCHECK: %[[LINEAR_STEP:.*]] = arith.constant 1 : i32
+!CHECK: %{{.*}} = arith.constant 1 : i32
+!CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32
+!CHECK: omp.distribute {
+!CHECK: omp.wsloop {
+!CHECK: omp.simd linear(%[[X]]#0 = %c1_i32 : !fir.ref<i32>, %[[I]]#0 = %c1_i32_1 : !fir.ref<i32>) private(@_QFteams_distribute_parallel_doEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
+ integer :: x
+ !$omp teams distribute parallel do simd linear(x)
+ do i = 1, N
+ end do
+ !$omp end teams distribute parallel do simd
+!CHECK: } {linear_var_types = [i32, i32], omp.composite}
+end subroutine teams_distribute_parallel_do
diff --git a/flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90 b/flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90
index c769152318663..120177a0420bf 100644
--- a/flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90
+++ b/flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90
@@ -11,7 +11,7 @@ subroutine distribute_parallel_do_simd_num_threads()
! CHECK: omp.parallel num_threads({{.*}}) {
! CHECK: omp.distribute {
! CHECK-NEXT: omp.wsloop {
- ! CHECK-NEXT: omp.simd private({{.*}}) {
+ ! CHECK-NEXT: omp.simd linear({{.*}}) private({{.*}}) {
! CHECK-NEXT: omp.loop_nest
!$omp distribute parallel do simd num_threads(10)
do index_ = 1, 10
@@ -28,7 +28,7 @@ subroutine distribute_parallel_do_simd_dist_schedule()
! CHECK: omp.parallel {
! CHECK: omp.distribute dist_schedule_static dist_schedule_chunk_size({{.*}}) {
! CHECK-NEXT: omp.wsloop {
- ! CHECK-NEXT: omp.simd private({{.*}}) {
+ ! CHECK-NEXT: omp.simd linear({{.*}}) private({{.*}}) {
! CHECK-NEXT: omp.loop_nest
!$omp distribute parallel do simd dist_schedule(static, 4)
do index_ = 1, 10
@@ -45,7 +45,7 @@ subroutine distribute_parallel_do_simd_schedule()
! CHECK: omp.parallel {
! CHECK: omp.distribute {
! CHECK-NEXT: omp.wsloop schedule(static = {{.*}}) {
- ! CHECK-NEXT: omp.simd private({{.*}}) {
+ ! CHECK-NEXT: omp.simd linear({{.*}}) private({{.*}}) {
! CHECK-NEXT: omp.loop_nest
!$omp distribute parallel do simd schedule(static, 4)
do index_ = 1, 10
@@ -62,7 +62,7 @@ subroutine distribute_parallel_do_simd_simdlen()
! CHECK: omp.parallel {
! CHECK: omp.distribute {
! CHECK-NEXT: omp.wsloop {
- ! CHECK-NEXT: omp.simd simdlen(4) private({{.*}}) {
+ ! CHECK-NEXT: omp.simd linear({{.*}}) simdlen(4) private({{.*}}) {
! CHECK-NEXT: omp.loop_nest
!$omp distribute parallel do simd simdlen(4)
do index_ = 1, 10
@@ -86,7 +86,7 @@ subroutine distribute_parallel_do_simd_private()
! CHECK: omp.parallel {
! CHECK: omp.distribute {
! CHECK-NEXT: omp.wsloop {
- ! CHECK-NEXT: omp.simd private(@{{.*}} %[[X]]#0 -> %[[X_ARG:[^,]+]],
+ ! CHECK-NEXT: omp.simd linear(%{{.*}}) private(@{{.*}} %[[X]]#0 -> %[[X_ARG:[^,]+]],
! CHECK-SAME: @{{.*}} %[[INDEX]]#0 -> %[[INDEX_ARG:.*]] : !fir.ref<i64>, !fir.ref<i32>) {
! CHECK-NEXT: omp.loop_nest
! CHECK: %[[X_PRIV:.*]]:2 = hlfir.declare %[[X_ARG]]
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index aad46ba094f7b..06cb1a72eea49 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -2665,6 +2665,11 @@ convertOmpWsloop(Operation &opInst, llvm::IRBuilderBase &builder,
if (!wsloopOp.getLinearVars().empty()) {
auto linearVarTypes = wsloopOp.getLinearVarTypes().value();
+ if (linearVarTypes.size() != wsloopOp.getLinearVars().size()) {
+ wsloopOp->emitError("Ill-formed type attributes for linear variables");
+ return failure();
+ }
+
for (mlir::Attribute linearVarType : linearVarTypes)
linearClauseProcessor.registerType(moduleTranslation, linearVarType);
@@ -2999,6 +3004,10 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
if (!simdOp.getLinearVars().empty()) {
auto linearVarTypes = simdOp.getLinearVarTypes().value();
+ if (linearVarTypes.size() != simdOp.getLinearVars().size()) {
+ simdOp->emitError("Ill-formed type attributes for linear variables");
+ return failure();
+ }
for (mlir::Attribute linearVarType : linearVarTypes)
linearClauseProcessor.registerType(moduleTranslation, linearVarType);
for (auto [idx, linearVar] : llvm::enumerate(simdOp.getLinearVars())) {
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
index 41bc5c4ba525f..df631d27f8536 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
@@ -89,3 +89,31 @@ llvm.func @omp_threadprivate() {
llvm.store %3, %5 : i32, !llvm.ptr
llvm.return
}
+
+// -----
+
+llvm.func @wsloop_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
+ // expected-error @below {{Ill-formed type attributes for linear variables}}
+ // expected-error @below {{LLVM Translation failed for operation: omp.wsloop}}
+ omp.wsloop linear(%x = %step : !llvm.ptr) {
+ omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
+ omp.yield
+ }
+ }
+ llvm.return
+}
+
+// -----
+
+llvm.func @simd_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
+ // expected-error @below {{Ill-formed type attributes for linear variables}}
+ // expected-error @below {{LLVM Translation failed for operation: omp.simd}}
+ omp.simd linear(%x = %step : !llvm.ptr) {
+ omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
+ omp.yield
+ }
+ }
+ llvm.return
+}
+
+
>From 4537e49f76b81ddaf87f021b6763315ef6151b4a Mon Sep 17 00:00:00 2001
From: NimishMishra <neelam.nimish at gmail.com>
Date: Thu, 8 Jan 2026 12:03:05 +0530
Subject: [PATCH 2/4] Update test and doc
---
flang/docs/OpenMPSupport.md | 2 +-
mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir | 6 ++----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md
index 74ef078fbf60f..274297a22140b 100644
--- a/flang/docs/OpenMPSupport.md
+++ b/flang/docs/OpenMPSupport.md
@@ -59,7 +59,7 @@ Note : No distinction is made between the support in Parser/Semantics, MLIR, Low
| teams distribute parallel loop construct | Y | |
| target teams distribute parallel loop construct | Y | |
| teams distribute parallel loop simd construct | P | Implicit linearization is skipped if iv is a pointer or allocatable |
-| target teams distribute parallel loop simd construct | P | linear clause is not supported |
+| target teams distribute parallel loop simd construct | P | Implicit linearization is completely skipped |
## Extensions
### ATOMIC construct
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
index df631d27f8536..eaad12a877c07 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
@@ -99,7 +99,7 @@ llvm.func @wsloop_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
omp.yield
}
- }
+ } {linear_var_types = []}
llvm.return
}
@@ -112,8 +112,6 @@ llvm.func @simd_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
omp.yield
}
- }
+ } {linear_var_types = []}
llvm.return
}
-
-
>From a391459387ba200593e5acc31501bee8c594e084 Mon Sep 17 00:00:00 2001
From: NimishMishra <neelam.nimish at gmail.com>
Date: Thu, 8 Jan 2026 12:17:48 +0530
Subject: [PATCH 3/4] Fix test
---
flang/test/Lower/OpenMP/composite_simd_linear.f90 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/test/Lower/OpenMP/composite_simd_linear.f90 b/flang/test/Lower/OpenMP/composite_simd_linear.f90
index c8ea0238c039b..801c3cb266f36 100644
--- a/flang/test/Lower/OpenMP/composite_simd_linear.f90
+++ b/flang/test/Lower/OpenMP/composite_simd_linear.f90
@@ -95,7 +95,7 @@ subroutine teams_distribute_parallel_do
!CHECK: %[[X:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFteams_distribute_parallel_doEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
!CHECK: omp.teams {
!CHECK: omp.parallel {
-gCHECK: %[[LINEAR_STEP:.*]] = arith.constant 1 : i32
+!CHECK: %[[LINEAR_STEP:.*]] = arith.constant 1 : i32
!CHECK: %{{.*}} = arith.constant 1 : i32
!CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32
!CHECK: omp.distribute {
>From e0916ef9c220aac298cf8d0442587e3e9652e277 Mon Sep 17 00:00:00 2001
From: NimishMishra <neelam.nimish at gmail.com>
Date: Fri, 9 Jan 2026 08:46:29 +0530
Subject: [PATCH 4/4] Address review comments
---
.../Lower/OpenMP/composite_simd_linear.f90 | 6 ++----
mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp | 6 ++++++
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 9 +--------
mlir/test/Dialect/OpenMP/ops.mlir | 19 ++++++++++---------
.../Target/LLVMIR/openmp-llvm-invalid.mlir | 4 +---
5 files changed, 20 insertions(+), 24 deletions(-)
diff --git a/flang/test/Lower/OpenMP/composite_simd_linear.f90 b/flang/test/Lower/OpenMP/composite_simd_linear.f90
index 801c3cb266f36..706b09d378db4 100644
--- a/flang/test/Lower/OpenMP/composite_simd_linear.f90
+++ b/flang/test/Lower/OpenMP/composite_simd_linear.f90
@@ -62,8 +62,7 @@ subroutine parallel_do
!CHECK: %{{.*}} = arith.constant 1 : i32
!CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32
!CHECK: omp.wsloop {
-!CHECK: omp.simd linear(%[[X]]#0 = %[[LINEAR_STEP]] : !fir.ref<i32>, %[[I]]#0 = %[[IV_STEP]] : !fir.ref<i32>)
-!private(@_QFparallel_doEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
+!CHECK: omp.simd linear(%[[X]]#0 = %[[LINEAR_STEP]] : !fir.ref<i32>, %[[I]]#0 = %[[IV_STEP]] : !fir.ref<i32>) private(@_QFparallel_doEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
integer :: x
!$omp parallel do simd linear(x:2)
do i = 1, N
@@ -80,8 +79,7 @@ subroutine teams_distribute
!CHECK: {{.*}} = arith.constant 1 : i32
!CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32
!CHECK: omp.distribute {
-!CHECK: omp.simd linear(%[[X]]#0 = %[[LINEAR_STEP]] : !fir.ref<i32>, %[[I]]#0 = %[[IV_STEP]] : !fir.ref<i32>)
-!private(@_QFteams_distributeEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
+!CHECK: omp.simd linear(%[[X]]#0 = %[[LINEAR_STEP]] : !fir.ref<i32>, %[[I]]#0 = %[[IV_STEP]] : !fir.ref<i32>) private(@_QFteams_distributeEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
integer :: x
!$omp teams distribute simd linear(x)
do i = 1, N
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index 103295d136dbb..933d4ee6302c7 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -2859,6 +2859,9 @@ void WsloopOp::build(OpBuilder &builder, OperationState &state,
}
LogicalResult WsloopOp::verify() {
+ if (getLinearVars().size() &&
+ getLinearVarTypes().value().size() != getLinearVars().size())
+ return emitError() << "Ill-formed type attributes for linear variables";
return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
getReductionByref());
}
@@ -2948,6 +2951,9 @@ LogicalResult SimdOp::verify() {
}
}
+ if (getLinearVars().size() &&
+ getLinearVarTypes().value().size() != getLinearVars().size())
+ return emitError() << "Ill-formed type attributes for linear variables";
return success();
}
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 06cb1a72eea49..bbcd8885a8885 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -2665,10 +2665,6 @@ convertOmpWsloop(Operation &opInst, llvm::IRBuilderBase &builder,
if (!wsloopOp.getLinearVars().empty()) {
auto linearVarTypes = wsloopOp.getLinearVarTypes().value();
- if (linearVarTypes.size() != wsloopOp.getLinearVars().size()) {
- wsloopOp->emitError("Ill-formed type attributes for linear variables");
- return failure();
- }
for (mlir::Attribute linearVarType : linearVarTypes)
linearClauseProcessor.registerType(moduleTranslation, linearVarType);
@@ -3004,10 +3000,7 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
if (!simdOp.getLinearVars().empty()) {
auto linearVarTypes = simdOp.getLinearVarTypes().value();
- if (linearVarTypes.size() != simdOp.getLinearVars().size()) {
- simdOp->emitError("Ill-formed type attributes for linear variables");
- return failure();
- }
+
for (mlir::Attribute linearVarType : linearVarTypes)
linearClauseProcessor.registerType(moduleTranslation, linearVarType);
for (auto [idx, linearVar] : llvm::enumerate(simdOp.getLinearVars())) {
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index ac29e20907b55..816df56ecc5a5 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -448,8 +448,8 @@ func.func @omp_wsloop(%lb : index, %ub : index, %step : index, %data_var : memre
omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
omp.yield
}
- }) {operandSegmentSizes = array<i32: 0,0,1,1,0,0,0>, schedule_kind = #omp<schedulekind static>} :
- (memref<i32>, i32) -> ()
+ }) {operandSegmentSizes = array<i32: 0,0,1,1,0,0,0>, schedule_kind = #omp<schedulekind static>,
+ linear_var_types = [i32]} : (memref<i32>, i32) -> ()
// CHECK: omp.wsloop linear(%{{.*}} = %{{.*}} : memref<i32>, %{{.*}} = %{{.*}} : memref<i32>) schedule(static) {
// CHECK-NEXT: omp.loop_nest
@@ -457,7 +457,8 @@ func.func @omp_wsloop(%lb : index, %ub : index, %step : index, %data_var : memre
omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
omp.yield
}
- }) {operandSegmentSizes = array<i32: 0,0,2,2,0,0,0>, schedule_kind = #omp<schedulekind static>} :
+ }) {operandSegmentSizes = array<i32: 0,0,2,2,0,0,0>, schedule_kind = #omp<schedulekind static>,
+ linear_var_types = [i32,i32]} :
(memref<i32>, memref<i32>, i32, i32) -> ()
// CHECK: omp.wsloop linear(%{{.*}} = %{{.*}} : memref<i32>) ordered(2) schedule(dynamic = %{{.*}}) {
@@ -466,8 +467,8 @@ func.func @omp_wsloop(%lb : index, %ub : index, %step : index, %data_var : memre
omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
omp.yield
}
- }) {operandSegmentSizes = array<i32: 0,0,1,1,0,0,1>, schedule_kind = #omp<schedulekind dynamic>, ordered = 2} :
- (memref<i32>, i32, i32) -> ()
+ }) {operandSegmentSizes = array<i32: 0,0,1,1,0,0,1>, schedule_kind = #omp<schedulekind dynamic>, ordered = 2,
+ linear_var_types = [i32]} : (memref<i32>, i32, i32) -> ()
// CHECK: omp.wsloop nowait schedule(auto) {
// CHECK-NEXT: omp.loop_nest
@@ -509,7 +510,7 @@ func.func @omp_wsloop_pretty(%lb : index, %ub : index, %step : index, %data_var
omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
omp.yield
}
- }
+ } { linear_var_types = [i32] }
// CHECK: omp.wsloop linear(%{{.*}} = %{{.*}} : memref<i32>) ordered(2) schedule(static = %{{.*}} : i32) {
// CHECK-NEXT: omp.loop_nest
@@ -517,7 +518,7 @@ func.func @omp_wsloop_pretty(%lb : index, %ub : index, %step : index, %data_var
omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
omp.yield
}
- }
+ } { linear_var_types = [i32] }
// CHECK: omp.wsloop linear(%{{.*}} = %{{.*}} : memref<i32>) ordered(2) schedule(dynamic = %{{.*}} : i32, nonmonotonic) {
// CHECK-NEXT: omp.loop_nest
@@ -525,7 +526,7 @@ func.func @omp_wsloop_pretty(%lb : index, %ub : index, %step : index, %data_var
omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
omp.yield
}
- }
+ } { linear_var_types = [i32] }
// CHECK: omp.wsloop linear(%{{.*}} = %{{.*}} : memref<i32>) ordered(2) schedule(dynamic = %{{.*}} : i16, monotonic) {
// CHECK-NEXT: omp.loop_nest
@@ -533,7 +534,7 @@ func.func @omp_wsloop_pretty(%lb : index, %ub : index, %step : index, %data_var
omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
omp.yield
}
- }
+ } { linear_var_types = [i32] }
// CHECK: omp.wsloop {
// CHECK-NEXT: omp.loop_nest
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
index eaad12a877c07..d1fbd7be5c627 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
@@ -94,7 +94,6 @@ llvm.func @omp_threadprivate() {
llvm.func @wsloop_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
// expected-error @below {{Ill-formed type attributes for linear variables}}
- // expected-error @below {{LLVM Translation failed for operation: omp.wsloop}}
omp.wsloop linear(%x = %step : !llvm.ptr) {
omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
omp.yield
@@ -106,8 +105,7 @@ llvm.func @wsloop_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
// -----
llvm.func @simd_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
- // expected-error @below {{Ill-formed type attributes for linear variables}}
- // expected-error @below {{LLVM Translation failed for operation: omp.simd}}
+ // expected-error @below {{Ill-formed type attributes for linear variables}}
omp.simd linear(%x = %step : !llvm.ptr) {
omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
omp.yield
More information about the Mlir-commits
mailing list