[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
Wed Jan 7 22:48:02 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/3] [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/3] 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/3] 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 {



More information about the Mlir-commits mailing list