[Mlir-commits] [mlir] [mlir][OpenMP] Fix update of linear iteration variables (PR #183800)

Leandro Lupori llvmlistbot at llvm.org
Fri Jul 24 07:59:45 PDT 2026


https://github.com/luporl updated https://github.com/llvm/llvm-project/pull/183800

>From 49d7a2054c0b123a6783a49383c71066b7e1d1d9 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Fri, 27 Feb 2026 15:32:58 -0300
Subject: [PATCH 1/6] [mlir][OpenMP] Fix update of linear iteration variables

The final value of a linear iteration variable must be the loop
limit_value + step. Before this patch it was limit_value.

This fixes the second issue reported in #170784.
---
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      | 129 ++++++++++++------
 mlir/test/Target/LLVMIR/openmp-llvm.mlir      |  15 +-
 2 files changed, 103 insertions(+), 41 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index deefa29c157f3..993cd89f74300 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -150,6 +150,37 @@ class LinearClauseProcessor {
   llvm::BasicBlock *linearFinalizationBB;
   llvm::BasicBlock *linearExitBB;
   llvm::BasicBlock *linearLastIterExitBB;
+  Value linearLoopIV;
+  Value linearLoopIVStart;
+
+  void updateLinearVar(llvm::IRBuilderBase &builder, llvm::Type *varType,
+                       llvm::Value *var, llvm::Value *varStart,
+                       llvm::Value *step, llvm::Value *iv) {
+    if (!iv->getType()->isIntegerTy())
+      llvm_unreachable("OpenMP loop induction variable must be an integer "
+                       "type");
+
+    if (varType->isIntegerTy()) {
+      // Integer path: normalize all arithmetic to linearVarType
+      iv = builder.CreateSExtOrTrunc(iv, varType);
+      step = builder.CreateSExtOrTrunc(step, varType);
+
+      llvm::Value *mulInst = builder.CreateMul(iv, step);
+      llvm::Value *addInst = builder.CreateAdd(varStart, mulInst);
+      builder.CreateStore(addInst, var);
+    } else if (varType->isFloatingPointTy()) {
+      // Float path: perform multiply in integer, then convert to float
+      step = builder.CreateSExtOrTrunc(step, iv->getType());
+
+      llvm::Value *mulInst = builder.CreateMul(iv, step);
+      llvm::Value *mulFp = builder.CreateSIToFP(mulInst, varType);
+      llvm::Value *addInst = builder.CreateFAdd(varStart, mulFp);
+      builder.CreateStore(addInst, var);
+    } else {
+      llvm_unreachable(
+          "Linear variable must be of integer or floating-point type");
+    }
+  }
 
 public:
   // Register type for the linear variables
@@ -189,46 +220,63 @@ class LinearClauseProcessor {
     }
   }
 
+  // Find linear iteration variable and save it for later updates
+  void initLinearIV(omp::SimdOp simdOp) {
+    auto loopOp = cast<omp::LoopNestOp>(simdOp.getWrappedLoop());
+    // NOTE iteration variables can only be linear in non-nested loops.
+    if (loopOp.getIVs().size() != 1)
+      return;
+    // The linear IV is the loop IV's store address.
+    BlockArgument arg = loopOp.getIVs().front();
+    for (const Operation *user : arg.getUsers()) {
+      if (auto storeOp = dyn_cast<LLVM::StoreOp>(user)) {
+        for (Value linearVar : simdOp.getLinearVars()) {
+          if (linearVar == storeOp.getAddr()) {
+            linearLoopIV = linearVar;
+            linearLoopIVStart = loopOp.getLoopLowerBounds().front();
+            break;
+          }
+        }
+      }
+    }
+  }
+
   // Emit IR for updating Linear variables
-  void updateLinearVar(llvm::IRBuilderBase &builder, llvm::BasicBlock *loopBody,
-                       llvm::Value *loopInductionVar) {
+  void updateLinearVars(llvm::IRBuilderBase &builder,
+                        llvm::BasicBlock *loopBody,
+                        llvm::Value *loopInductionVar) {
     builder.SetInsertPoint(loopBody->getTerminator());
     for (size_t index = 0; index < linearPreconditionVars.size(); index++) {
-      llvm::Type *linearVarType = linearVarTypes[index];
-      llvm::Value *iv = loopInductionVar;
-      llvm::Value *step = linearSteps[index];
-
-      if (!iv->getType()->isIntegerTy())
-        llvm_unreachable("OpenMP loop induction variable must be an integer "
-                         "type");
-
-      if (linearVarType->isIntegerTy()) {
-        // Integer path: normalize all arithmetic to linearVarType
-        iv = builder.CreateSExtOrTrunc(iv, linearVarType);
-        step = builder.CreateSExtOrTrunc(step, linearVarType);
-
-        llvm::LoadInst *linearVarStart =
-            builder.CreateLoad(linearVarType, linearPreconditionVars[index]);
-        llvm::Value *mulInst = builder.CreateMul(iv, step);
-        llvm::Value *addInst = builder.CreateAdd(linearVarStart, mulInst);
-        builder.CreateStore(addInst, linearLoopBodyTemps[index]);
-      } else if (linearVarType->isFloatingPointTy()) {
-        // Float path: perform multiply in integer, then convert to float
-        step = builder.CreateSExtOrTrunc(step, iv->getType());
-        llvm::Value *mulInst = builder.CreateMul(iv, step);
-
-        llvm::LoadInst *linearVarStart =
-            builder.CreateLoad(linearVarType, linearPreconditionVars[index]);
-        llvm::Value *mulFp = builder.CreateSIToFP(mulInst, linearVarType);
-        llvm::Value *addInst = builder.CreateFAdd(linearVarStart, mulFp);
-        builder.CreateStore(addInst, linearLoopBodyTemps[index]);
-      } else {
-        llvm_unreachable(
-            "Linear variable must be of integer or floating-point type");
-      }
+      llvm::LoadInst *linearVarStart = builder.CreateLoad(
+          linearVarTypes[index], linearPreconditionVars[index]);
+      updateLinearVar(builder, linearVarTypes[index],
+                      linearLoopBodyTemps[index], linearVarStart,
+                      linearSteps[index], loopInductionVar);
     }
   }
 
+  // Emit IR for updating linear iteration variables on loop exit
+  void updateLinearIV(llvm::IRBuilderBase &builder,
+                      LLVM::ModuleTranslation &moduleTranslation,
+                      llvm::Value *loopIV) {
+    if (!linearLoopIV)
+      return;
+    llvm::Value *linearIV = moduleTranslation.lookupValue(linearLoopIV);
+    llvm::Value *linearIVStart =
+        moduleTranslation.lookupValue(linearLoopIVStart);
+
+    // Find linearIV's index
+    size_t index;
+    for (index = 0; index < linearOrigVal.size(); index++)
+      if (linearIV == linearOrigVal[index])
+        break;
+    if (index == linearOrigVal.size())
+      return;
+
+    updateLinearVar(builder, linearVarTypes[index], linearLoopBodyTemps[index],
+                    linearIVStart, linearSteps[index], loopIV);
+  }
+
   // Linear variable finalization is conditional on the last logical iteration.
   // Create BB splits to manage the same.
   void splitLinearFiniBB(llvm::IRBuilderBase &builder,
@@ -3852,8 +3900,8 @@ convertOmpWsloop(Operation &opInst, llvm::IRBuilderBase &builder,
     if (failed(handleError(afterBarrierIP, *loopOp)))
       return failure();
     builder.restoreIP(*afterBarrierIP);
-    linearClauseProcessor.updateLinearVar(builder, loopInfo->getBody(),
-                                          loopInfo->getIndVar());
+    linearClauseProcessor.updateLinearVars(builder, loopInfo->getBody(),
+                                           loopInfo->getIndVar());
     linearClauseProcessor.splitLinearFiniBB(builder, loopInfo->getExit());
   }
 
@@ -4153,6 +4201,8 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
   // Initialize linear variables and linear step
   LinearClauseProcessor linearClauseProcessor;
 
+  linearClauseProcessor.initLinearIV(simdOp);
+
   if (!simdOp.getLinearVars().empty()) {
     auto linearVarTypes = simdOp.getLinearVarTypes().value();
     for (mlir::Attribute linearVarType : linearVarTypes)
@@ -4250,8 +4300,8 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
     linearClauseProcessor.initLinearVar(builder, moduleTranslation,
                                         loopInfo->getPreheader());
 
-    linearClauseProcessor.updateLinearVar(builder, loopInfo->getBody(),
-                                          loopInfo->getIndVar());
+    linearClauseProcessor.updateLinearVars(builder, loopInfo->getBody(),
+                                           loopInfo->getIndVar());
   }
   builder.SetInsertPoint(*regionBlock, (*regionBlock)->begin());
 
@@ -4261,6 +4311,9 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
                             : nullptr,
                         order, simdlen, safelen);
 
+  linearClauseProcessor.updateLinearIV(builder, moduleTranslation,
+                                       loopInfo->getIndVar());
+
   linearClauseProcessor.emitStoresForLinearVar(builder);
 
   // Check if this SIMD loop contains ordered regions
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
index 38f10320bd3ae..45eb6ac1ef957 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -743,6 +743,7 @@ llvm.func @simd_simple(%lb : i64, %ub : i64, %step : i64, %arg0: !llvm.ptr) {
 llvm.func @simd_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
 
 // CHECK-LABEL: @simd_linear
+// CHECK-SAME: (i32 %[[LB:.*]], i32 %{{.*}}, i32 %[[STEP:.*]], ptr %[[X:.*]])
 
 // CHECK: %[[LINEAR_VAR:.*]] = alloca i32, align 4
 // CHECK: %[[LINEAR_RESULT:.*]] = alloca i32, align 4
@@ -758,8 +759,16 @@ llvm.func @simd_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
 // CHECK: %[[MUL:.*]] = mul i32 %omp_loop.iv, {{.*}}
 // CHECK: %[[ADD:.*]] = add i32 %[[LOAD]], %[[MUL]]
 // CHECK: store i32 %[[ADD]], ptr %[[LINEAR_RESULT]], align 4, !llvm.access.group !1
+
+// CHECK: omp.region.cont:
+// CHECK: %[[MUL:.*]] = mul i32 %omp_loop.iv, %[[STEP]]
+// CHECK: %[[ADD:.*]] = add i32 %[[LB]], %[[MUL]]
+// CHECK: store i32 %[[ADD]], ptr %[[LINEAR_RESULT]], align 4
+// CHECK: %[[LOAD:.*]] = load i32, ptr %[[LINEAR_RESULT]], align 4
+// CHECK: store i32 %[[LOAD]], ptr %[[X]], align 4
   omp.simd linear(%x : !llvm.ptr = %step : i32) {
     omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
+      llvm.store %iv, %x : i32, !llvm.ptr
       omp.yield
     }
   } {linear_var_types = [i32]}
@@ -784,8 +793,8 @@ llvm.func @simd_linear_i64_var_i32_step(%lb : i32, %ub : i32, %x : !llvm.ptr) {
 
 // CHECK: omp_loop.body:
 // Verify type conversions: iv (i32) is extended to i64 before multiplication
-// CHECK: %[[IV_I64:.*]] = sext i32 %omp_loop.iv to i64
 // CHECK: %[[LOAD:.*]] = load i64, ptr %[[LINEAR_VAR]], {{.*}}!llvm.access.group
+// CHECK: %[[IV_I64:.*]] = sext i32 %omp_loop.iv to i64
 // Verify multiplication and addition use consistent i64 types
 // CHECK: %[[MUL:.*]] = mul i64 %[[IV_I64]], {{.*}}
 // CHECK: %[[ADD:.*]] = add i64 %[[LOAD]], %[[MUL]]
@@ -817,8 +826,8 @@ llvm.func @simd_linear_f64_var_i32_step(%lb : i32, %ub : i32, %x : !llvm.ptr) {
 // CHECK: omp_loop.body:
 // Verify integer multiplication, load, and conversion to float
 // CHECK: mul i32 %omp_loop.iv
-// CHECK: %[[MUL_INT:.*]] = mul i32 %omp_loop.iv, {{.*}}
-// CHECK-NEXT: %[[LOAD:.*]] = load double, ptr %[[LINEAR_VAR]], {{.*}}!llvm.access.group
+// CHECK: %[[LOAD:.*]] = load double, ptr %[[LINEAR_VAR]], {{.*}}!llvm.access.group
+// CHECK-NEXT: %[[MUL_INT:.*]] = mul i32 %omp_loop.iv, {{.*}}
 // CHECK-NEXT: %[[MUL_FP:.*]] = sitofp i32 %[[MUL_INT]] to double
 // CHECK-NEXT: %[[ADD:.*]] = fadd double %[[LOAD]], %[[MUL_FP]]
 // CHECK-NEXT: store double %[[ADD]], ptr %[[LINEAR_RESULT]], {{.*}}!llvm.access.group

>From 20ac4aef52312cd93944ad17f0fef0b126211102 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Tue, 19 May 2026 12:06:23 -0300
Subject: [PATCH 2/6] Fix "instruction does not dominate all uses" issue

---
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      | 104 +++++++++---------
 mlir/test/Target/LLVMIR/openmp-llvm.mlir      |  48 ++++++--
 2 files changed, 90 insertions(+), 62 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 993cd89f74300..aace5283e4362 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -151,36 +151,6 @@ class LinearClauseProcessor {
   llvm::BasicBlock *linearExitBB;
   llvm::BasicBlock *linearLastIterExitBB;
   Value linearLoopIV;
-  Value linearLoopIVStart;
-
-  void updateLinearVar(llvm::IRBuilderBase &builder, llvm::Type *varType,
-                       llvm::Value *var, llvm::Value *varStart,
-                       llvm::Value *step, llvm::Value *iv) {
-    if (!iv->getType()->isIntegerTy())
-      llvm_unreachable("OpenMP loop induction variable must be an integer "
-                       "type");
-
-    if (varType->isIntegerTy()) {
-      // Integer path: normalize all arithmetic to linearVarType
-      iv = builder.CreateSExtOrTrunc(iv, varType);
-      step = builder.CreateSExtOrTrunc(step, varType);
-
-      llvm::Value *mulInst = builder.CreateMul(iv, step);
-      llvm::Value *addInst = builder.CreateAdd(varStart, mulInst);
-      builder.CreateStore(addInst, var);
-    } else if (varType->isFloatingPointTy()) {
-      // Float path: perform multiply in integer, then convert to float
-      step = builder.CreateSExtOrTrunc(step, iv->getType());
-
-      llvm::Value *mulInst = builder.CreateMul(iv, step);
-      llvm::Value *mulFp = builder.CreateSIToFP(mulInst, varType);
-      llvm::Value *addInst = builder.CreateFAdd(varStart, mulFp);
-      builder.CreateStore(addInst, var);
-    } else {
-      llvm_unreachable(
-          "Linear variable must be of integer or floating-point type");
-    }
-  }
 
 public:
   // Register type for the linear variables
@@ -233,7 +203,6 @@ class LinearClauseProcessor {
         for (Value linearVar : simdOp.getLinearVars()) {
           if (linearVar == storeOp.getAddr()) {
             linearLoopIV = linearVar;
-            linearLoopIVStart = loopOp.getLoopLowerBounds().front();
             break;
           }
         }
@@ -242,28 +211,51 @@ class LinearClauseProcessor {
   }
 
   // Emit IR for updating Linear variables
-  void updateLinearVars(llvm::IRBuilderBase &builder,
-                        llvm::BasicBlock *loopBody,
-                        llvm::Value *loopInductionVar) {
+  void updateLinearVar(llvm::IRBuilderBase &builder, llvm::BasicBlock *loopBody,
+                       llvm::Value *loopInductionVar) {
     builder.SetInsertPoint(loopBody->getTerminator());
     for (size_t index = 0; index < linearPreconditionVars.size(); index++) {
-      llvm::LoadInst *linearVarStart = builder.CreateLoad(
-          linearVarTypes[index], linearPreconditionVars[index]);
-      updateLinearVar(builder, linearVarTypes[index],
-                      linearLoopBodyTemps[index], linearVarStart,
-                      linearSteps[index], loopInductionVar);
+      llvm::Type *linearVarType = linearVarTypes[index];
+      llvm::Value *iv = loopInductionVar;
+      llvm::Value *step = linearSteps[index];
+
+      if (!iv->getType()->isIntegerTy())
+        llvm_unreachable("OpenMP loop induction variable must be an integer "
+                         "type");
+
+      if (linearVarType->isIntegerTy()) {
+        // Integer path: normalize all arithmetic to linearVarType
+        iv = builder.CreateSExtOrTrunc(iv, linearVarType);
+        step = builder.CreateSExtOrTrunc(step, linearVarType);
+
+        llvm::LoadInst *linearVarStart =
+            builder.CreateLoad(linearVarType, linearPreconditionVars[index]);
+        llvm::Value *mulInst = builder.CreateMul(iv, step);
+        llvm::Value *addInst = builder.CreateAdd(linearVarStart, mulInst);
+        builder.CreateStore(addInst, linearLoopBodyTemps[index]);
+      } else if (linearVarType->isFloatingPointTy()) {
+        // Float path: perform multiply in integer, then convert to float
+        step = builder.CreateSExtOrTrunc(step, iv->getType());
+        llvm::Value *mulInst = builder.CreateMul(iv, step);
+
+        llvm::LoadInst *linearVarStart =
+            builder.CreateLoad(linearVarType, linearPreconditionVars[index]);
+        llvm::Value *mulFp = builder.CreateSIToFP(mulInst, linearVarType);
+        llvm::Value *addInst = builder.CreateFAdd(linearVarStart, mulFp);
+        builder.CreateStore(addInst, linearLoopBodyTemps[index]);
+      } else {
+        llvm_unreachable(
+            "Linear variable must be of integer or floating-point type");
+      }
     }
   }
 
   // Emit IR for updating linear iteration variables on loop exit
   void updateLinearIV(llvm::IRBuilderBase &builder,
-                      LLVM::ModuleTranslation &moduleTranslation,
-                      llvm::Value *loopIV) {
+                      LLVM::ModuleTranslation &moduleTranslation) {
     if (!linearLoopIV)
       return;
     llvm::Value *linearIV = moduleTranslation.lookupValue(linearLoopIV);
-    llvm::Value *linearIVStart =
-        moduleTranslation.lookupValue(linearLoopIVStart);
 
     // Find linearIV's index
     size_t index;
@@ -273,8 +265,17 @@ class LinearClauseProcessor {
     if (index == linearOrigVal.size())
       return;
 
-    updateLinearVar(builder, linearVarTypes[index], linearLoopBodyTemps[index],
-                    linearIVStart, linearSteps[index], loopIV);
+    // Add one more step to the linear iteration variable
+    llvm::Type *varType = linearVarTypes[index];
+    llvm::Value *var = linearLoopBodyTemps[index];
+    llvm::Value *step = linearSteps[index];
+    if (!varType->isIntegerTy())
+      llvm_unreachable("Linear iteration variable must be of integer type");
+
+    step = builder.CreateSExtOrTrunc(step, varType);
+    llvm::Value *val = builder.CreateLoad(varType, var);
+    llvm::Value *addInst = builder.CreateAdd(val, step);
+    builder.CreateStore(addInst, var);
   }
 
   // Linear variable finalization is conditional on the last logical iteration.
@@ -3900,8 +3901,8 @@ convertOmpWsloop(Operation &opInst, llvm::IRBuilderBase &builder,
     if (failed(handleError(afterBarrierIP, *loopOp)))
       return failure();
     builder.restoreIP(*afterBarrierIP);
-    linearClauseProcessor.updateLinearVars(builder, loopInfo->getBody(),
-                                           loopInfo->getIndVar());
+    linearClauseProcessor.updateLinearVar(builder, loopInfo->getBody(),
+                                          loopInfo->getIndVar());
     linearClauseProcessor.splitLinearFiniBB(builder, loopInfo->getExit());
   }
 
@@ -4200,7 +4201,6 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
 
   // Initialize linear variables and linear step
   LinearClauseProcessor linearClauseProcessor;
-
   linearClauseProcessor.initLinearIV(simdOp);
 
   if (!simdOp.getLinearVars().empty()) {
@@ -4300,8 +4300,8 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
     linearClauseProcessor.initLinearVar(builder, moduleTranslation,
                                         loopInfo->getPreheader());
 
-    linearClauseProcessor.updateLinearVars(builder, loopInfo->getBody(),
-                                           loopInfo->getIndVar());
+    linearClauseProcessor.updateLinearVar(builder, loopInfo->getBody(),
+                                          loopInfo->getIndVar());
   }
   builder.SetInsertPoint(*regionBlock, (*regionBlock)->begin());
 
@@ -4311,9 +4311,7 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
                             : nullptr,
                         order, simdlen, safelen);
 
-  linearClauseProcessor.updateLinearIV(builder, moduleTranslation,
-                                       loopInfo->getIndVar());
-
+  linearClauseProcessor.updateLinearIV(builder, moduleTranslation);
   linearClauseProcessor.emitStoresForLinearVar(builder);
 
   // Check if this SIMD loop contains ordered regions
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
index 45eb6ac1ef957..51a12e23a0e5e 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -743,7 +743,7 @@ llvm.func @simd_simple(%lb : i64, %ub : i64, %step : i64, %arg0: !llvm.ptr) {
 llvm.func @simd_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
 
 // CHECK-LABEL: @simd_linear
-// CHECK-SAME: (i32 %[[LB:.*]], i32 %{{.*}}, i32 %[[STEP:.*]], ptr %[[X:.*]])
+// CHECK-SAME: (i32 %{{.*}}, i32 %{{.*}}, i32 %[[STEP:.*]], ptr %[[X:.*]])
 
 // CHECK: %[[LINEAR_VAR:.*]] = alloca i32, align 4
 // CHECK: %[[LINEAR_RESULT:.*]] = alloca i32, align 4
@@ -761,11 +761,11 @@ llvm.func @simd_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
 // CHECK: store i32 %[[ADD]], ptr %[[LINEAR_RESULT]], align 4, !llvm.access.group !1
 
 // CHECK: omp.region.cont:
-// CHECK: %[[MUL:.*]] = mul i32 %omp_loop.iv, %[[STEP]]
-// CHECK: %[[ADD:.*]] = add i32 %[[LB]], %[[MUL]]
-// CHECK: store i32 %[[ADD]], ptr %[[LINEAR_RESULT]], align 4
-// CHECK: %[[LOAD:.*]] = load i32, ptr %[[LINEAR_RESULT]], align 4
-// CHECK: store i32 %[[LOAD]], ptr %[[X]], align 4
+// CHECK: %[[VAL:.*]] = load i32, ptr %[[LINEAR_RESULT]]
+// CHECK-NEXT: %[[ADD:.*]] = add i32 %[[VAL]], %[[STEP]]
+// CHECK-NEXT: store i32 %[[ADD]], ptr %[[LINEAR_RESULT]]
+// CHECK-NEXT: %[[LOAD:.*]] = load i32, ptr %[[LINEAR_RESULT]]
+// CHECK-NEXT: store i32 %[[LOAD]], ptr %[[X]], align 4
   omp.simd linear(%x : !llvm.ptr = %step : i32) {
     omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
       llvm.store %iv, %x : i32, !llvm.ptr
@@ -793,8 +793,8 @@ llvm.func @simd_linear_i64_var_i32_step(%lb : i32, %ub : i32, %x : !llvm.ptr) {
 
 // CHECK: omp_loop.body:
 // Verify type conversions: iv (i32) is extended to i64 before multiplication
-// CHECK: %[[LOAD:.*]] = load i64, ptr %[[LINEAR_VAR]], {{.*}}!llvm.access.group
 // CHECK: %[[IV_I64:.*]] = sext i32 %omp_loop.iv to i64
+// CHECK: %[[LOAD:.*]] = load i64, ptr %[[LINEAR_VAR]], {{.*}}!llvm.access.group
 // Verify multiplication and addition use consistent i64 types
 // CHECK: %[[MUL:.*]] = mul i64 %[[IV_I64]], {{.*}}
 // CHECK: %[[ADD:.*]] = add i64 %[[LOAD]], %[[MUL]]
@@ -826,8 +826,8 @@ llvm.func @simd_linear_f64_var_i32_step(%lb : i32, %ub : i32, %x : !llvm.ptr) {
 // CHECK: omp_loop.body:
 // Verify integer multiplication, load, and conversion to float
 // CHECK: mul i32 %omp_loop.iv
-// CHECK: %[[LOAD:.*]] = load double, ptr %[[LINEAR_VAR]], {{.*}}!llvm.access.group
-// CHECK-NEXT: %[[MUL_INT:.*]] = mul i32 %omp_loop.iv, {{.*}}
+// CHECK: %[[MUL_INT:.*]] = mul i32 %omp_loop.iv, {{.*}}
+// CHECK-NEXT: %[[LOAD:.*]] = load double, ptr %[[LINEAR_VAR]], {{.*}}!llvm.access.group
 // CHECK-NEXT: %[[MUL_FP:.*]] = sitofp i32 %[[MUL_INT]] to double
 // CHECK-NEXT: %[[ADD:.*]] = fadd double %[[LOAD]], %[[MUL_FP]]
 // CHECK-NEXT: store double %[[ADD]], ptr %[[LINEAR_RESULT]], {{.*}}!llvm.access.group
@@ -841,6 +841,36 @@ llvm.func @simd_linear_f64_var_i32_step(%lb : i32, %ub : i32, %x : !llvm.ptr) {
 
 // -----
 
+// Test the update of omp.simd linear iteration variables, when nested inside
+// omp.wsloop.
+llvm.func @wsloop_simd_linear(%x : !llvm.ptr) {
+
+// CHECK-LABEL: @wsloop_simd_linear
+
+// CHECK: omp.wsloop.region:
+// CHECK: %[[LINEAR_VAR:.*]] = alloca i32
+// CHECK: %[[LINEAR_RESULT:.*]] = alloca i32
+
+// CHECK: omp.region.cont2:
+// CHECK: %[[VAL:.*]] = load i32, ptr %[[LINEAR_RESULT]]
+// CHECK-NEXT: %[[ADD:.*]] = add i32 %[[VAL]], 25
+// CHECK-NEXT: store i32 %[[ADD]], ptr %[[LINEAR_RESULT]]
+  %lb = llvm.mlir.constant(1 : i32) : i32
+  %ub = llvm.mlir.constant(100 : i32) : i32
+  %step = llvm.mlir.constant(25 : i32) : i32
+  omp.wsloop {
+    omp.simd linear(%x : !llvm.ptr = %step : i32) {
+      omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
+        llvm.store %iv, %x : i32, !llvm.ptr
+        omp.yield
+      }
+    } {linear_var_types = [i32], omp.composite}
+  } {omp.composite}
+  llvm.return
+}
+
+// -----
+
 // CHECK-LABEL: @simd_simple_multiple
 llvm.func @simd_simple_multiple(%lb1 : i64, %ub1 : i64, %step1 : i64, %lb2 : i64, %ub2 : i64, %step2 : i64, %arg0: !llvm.ptr, %arg1: !llvm.ptr) {
   omp.simd {

>From 030a957a941fb50d15c99f9d0031e26019b53992 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Tue, 16 Jun 2026 09:58:04 -0300
Subject: [PATCH 3/6] Verify that linear variables don't appear in other DSA
 clauses

---
 mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp     | 10 ++++++++++
 mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir | 14 ++++++++++++++
 mlir/test/Target/LLVMIR/openmp-simd-ordered.mlir | 12 +++++-------
 3 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index db5fd8f2e3230..b328e15313538 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -3363,6 +3363,16 @@ LogicalResult SimdOp::verify() {
   if (getLinearVars().size() &&
       getLinearVarTypes().value().size() != getLinearVars().size())
     return emitError() << "Ill-formed type attributes for linear variables";
+
+  llvm::DenseSet<Value> privateVars(llvm::from_range, getPrivateVars());
+  llvm::DenseSet<Value> reductionVars(llvm::from_range, getReductionVars());
+  // TODO Check lastprivate vars when their support is added to SimdOp.
+  for (Value var : getLinearVars()) {
+    if (privateVars.contains(var) || reductionVars.contains(var))
+      return emitOpError()
+             << "linear variables cannot appear in other data-sharing clauses";
+  }
+
   return success();
 }
 
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
index d9459ace54422..7e2e0061f80be 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
@@ -116,6 +116,20 @@ llvm.func @simd_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
 
 // -----
 
+omp.private {type = private} @i_private_i32 : i32
+llvm.func @simd_linear_private(%lb : i32, %ub : i32, %step : i32, %i : !llvm.ptr) {
+  // expected-error @below {{linear variables cannot appear in other data-sharing clauses}}
+  omp.simd linear(%i : !llvm.ptr = %step : i32)
+           private(@i_private_i32 %i -> %priv_i : !llvm.ptr) {
+    omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
+      omp.yield
+    }
+  } {linear_var_types = [i32]}
+  llvm.return
+}
+
+// -----
+
 module attributes {llvm.target_triple = "amdgcn-amd-amdhsa", omp.is_target_device = true} {
   llvm.func @host_op_in_device(%arg0 : !llvm.ptr) {
     // expected-error @below {{unsupported host op found in device}}
diff --git a/mlir/test/Target/LLVMIR/openmp-simd-ordered.mlir b/mlir/test/Target/LLVMIR/openmp-simd-ordered.mlir
index 8da1fab1f48af..a84c51f08daa7 100644
--- a/mlir/test/Target/LLVMIR/openmp-simd-ordered.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-simd-ordered.mlir
@@ -17,8 +17,6 @@
 // !$omp end simd
 
 module {
-  omp.private {type = private} @i_private_i32 : i32
-
   // CHECK-LABEL: define void @simd_ordered_linear
   llvm.func @simd_ordered_linear() {
     %c0_i64 = llvm.mlir.constant(0 : i64) : i64
@@ -38,14 +36,14 @@ module {
     // CHECK: %.linear_var = alloca i32
     // CHECK: %.linear_result = alloca i32
 
-    omp.simd linear(%i : !llvm.ptr = %c1_i32 : i32) private(@i_private_i32 %i -> %arg0 : !llvm.ptr) {
+    omp.simd linear(%i : !llvm.ptr = %c1_i32 : i32) {
       omp.loop_nest (%iv) : i32 = (%c1_i32) to (%c10_i32) inclusive step (%c1_i32) {
         // CHECK: omp.loop_nest.region:
         // CHECK: load i32, ptr %.linear_result
-        llvm.store %iv, %arg0 : i32, !llvm.ptr
+        llvm.store %iv, %i : i32, !llvm.ptr
 
         // Compute a[i] = b[i] * 10
-        %i_val = llvm.load %arg0 : !llvm.ptr -> i32
+        %i_val = llvm.load %i : !llvm.ptr -> i32
         %i_idx = llvm.sext %i_val : i32 to i64
         %i_off = llvm.sub %i_idx, %c1_i64 : i64
         %b_ptr = llvm.getelementptr %b[%i_off] : (!llvm.ptr, i64) -> !llvm.ptr, i32
@@ -58,7 +56,7 @@ module {
         omp.ordered.region par_level_simd {
           // CHECK: omp.ordered.region:
           // CHECK: load i32, ptr %.linear_result
-          %i_ord = llvm.load %arg0 : !llvm.ptr -> i32
+          %i_ord = llvm.load %i : !llvm.ptr -> i32
           %i_ord_idx = llvm.sext %i_ord : i32 to i64
           %i_ord_off = llvm.sub %i_ord_idx, %c1_i64 : i64
           %a_ord_ptr = llvm.getelementptr %a[%i_ord_off] : (!llvm.ptr, i64) -> !llvm.ptr, i32
@@ -69,7 +67,7 @@ module {
         // Compute c[i] = a[i] * 2 (code after ordered region)
         // CHECK: omp_region.finalize:
         // CHECK: load i32, ptr %.linear_result
-        %i_post = llvm.load %arg0 : !llvm.ptr -> i32
+        %i_post = llvm.load %i : !llvm.ptr -> i32
         %i_post_idx = llvm.sext %i_post : i32 to i64
         %i_post_off = llvm.sub %i_post_idx, %c1_i64 : i64
         %a_post_ptr = llvm.getelementptr %a[%i_post_off] : (!llvm.ptr, i64) -> !llvm.ptr, i32

>From 2f8b6c3b6f7ca958505c6e6d7cc41ec53cb0fe0d Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Tue, 16 Jun 2026 11:10:29 -0300
Subject: [PATCH 4/6] Update mlir tests

---
 mlir/test/Target/LLVMIR/openmp-simd-guided.mlir      |  3 +--
 .../Target/LLVMIR/openmp-wsloop-simd-ordered.mlir    | 12 +++++-------
 2 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/mlir/test/Target/LLVMIR/openmp-simd-guided.mlir b/mlir/test/Target/LLVMIR/openmp-simd-guided.mlir
index b1f18b9f85417..496669c5b90d5 100644
--- a/mlir/test/Target/LLVMIR/openmp-simd-guided.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-simd-guided.mlir
@@ -1,7 +1,6 @@
 // Ensure that schedule can be used with the guided kind-type and simd construct
 // RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s
 
-omp.private {type = private} @_QFEi_private_i32 : i32
 llvm.func @test_simd_guided() {
   %0 = llvm.mlir.constant (1 : i64) : i64
   %c1_i32 = llvm.mlir.constant (1 : i32) : i32
@@ -10,7 +9,7 @@ llvm.func @test_simd_guided() {
   %c4_i32 = llvm.mlir.constant (4 : i32) : i32
   %1 = llvm.alloca %0 x i32 {bindc_name = "i"} : (i64) -> !llvm.ptr
   omp.wsloop schedule(guided = %c4_i32 : i32, simd) {
-    omp.simd linear(%1 : !llvm.ptr = %c1_i32 : i32) private(@_QFEi_private_i32 %1 -> %arg0 : !llvm.ptr) {
+    omp.simd linear(%1 : !llvm.ptr = %c1_i32 : i32) {
       omp.loop_nest (%arg1) : i32 = (%c0_i32) to (%c64_i32) inclusive step (%c1_i32) {
         omp.yield
       }
diff --git a/mlir/test/Target/LLVMIR/openmp-wsloop-simd-ordered.mlir b/mlir/test/Target/LLVMIR/openmp-wsloop-simd-ordered.mlir
index beb300c23651f..8d5b89ecbd00a 100644
--- a/mlir/test/Target/LLVMIR/openmp-wsloop-simd-ordered.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-wsloop-simd-ordered.mlir
@@ -17,8 +17,6 @@
 // !$omp end do simd
 
 module {
-  omp.private {type = private} @i_private_i32 : i32
-
   // CHECK-LABEL: define void @wsloop_simd_ordered_linear
   llvm.func @wsloop_simd_ordered_linear() {
     %c0_i64 = llvm.mlir.constant(0 : i64) : i64
@@ -39,14 +37,14 @@ module {
     // CHECK: %.linear_result = alloca i32
 
     omp.wsloop ordered(0) {
-      omp.simd linear(%i : !llvm.ptr = %c1_i32 : i32) private(@i_private_i32 %i -> %arg0 : !llvm.ptr) {
+      omp.simd linear(%i : !llvm.ptr = %c1_i32 : i32) {
         omp.loop_nest (%iv) : i32 = (%c1_i32) to (%c100_i32) inclusive step (%c1_i32) {
           // CHECK: omp.loop_nest.region:
           // CHECK: load i32, ptr %.linear_result
-          llvm.store %iv, %arg0 : i32, !llvm.ptr
+          llvm.store %iv, %i : i32, !llvm.ptr
 
           // Compute a[i] = b[i] * 10
-          %i_val = llvm.load %arg0 : !llvm.ptr -> i32
+          %i_val = llvm.load %i : !llvm.ptr -> i32
           %i_idx = llvm.sext %i_val : i32 to i64
           %i_off = llvm.sub %i_idx, %c1_i64 : i64
           %b_ptr = llvm.getelementptr %b[%i_off] : (!llvm.ptr, i64) -> !llvm.ptr, i32
@@ -59,7 +57,7 @@ module {
           omp.ordered.region par_level_simd {
             // CHECK: omp.ordered.region:
             // CHECK: load i32, ptr %.linear_result
-            %i_ord = llvm.load %arg0 : !llvm.ptr -> i32
+            %i_ord = llvm.load %i : !llvm.ptr -> i32
             %i_ord_idx = llvm.sext %i_ord : i32 to i64
             %i_ord_off = llvm.sub %i_ord_idx, %c1_i64 : i64
             %a_ord_ptr = llvm.getelementptr %a[%i_ord_off] : (!llvm.ptr, i64) -> !llvm.ptr, i32
@@ -71,7 +69,7 @@ module {
           // Compute c[i] = a[i] * 2 (code after ordered region)
           // CHECK: omp_region.finalize:
           // CHECK: load i32, ptr %.linear_result
-          %i_post = llvm.load %arg0 : !llvm.ptr -> i32
+          %i_post = llvm.load %i : !llvm.ptr -> i32
           %i_post_idx = llvm.sext %i_post : i32 to i64
           %i_post_off = llvm.sub %i_post_idx, %c1_i64 : i64
           %a_post_ptr = llvm.getelementptr %a[%i_post_off] : (!llvm.ptr, i64) -> !llvm.ptr, i32

>From b671d6cf6efa8413abd061ac101b1ea14137ff5f Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Thu, 23 Jul 2026 16:48:58 -0300
Subject: [PATCH 5/6] Report an error if the linear variable can't be
 determined unambiguously

---
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      | 20 ++++++++++++++-----
 .../Target/LLVMIR/openmp-llvm-invalid.mlir    | 15 ++++++++++++++
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 2a246ebf98b3c..b938572fe3720 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -191,23 +191,32 @@ class LinearClauseProcessor {
   }
 
   // Find linear iteration variable and save it for later updates
-  void initLinearIV(omp::SimdOp simdOp) {
+  LogicalResult initLinearIV(omp::SimdOp simdOp) {
     auto loopOp = cast<omp::LoopNestOp>(simdOp.getWrappedLoop());
     // NOTE iteration variables can only be linear in non-nested loops.
     if (loopOp.getIVs().size() != 1)
-      return;
-    // The linear IV is the loop IV's store address.
+      return success();
+    // Currently, frontends using `omp.simd` always generate a store from the
+    // `omp.loop_nest`'s IV to the corresponding iteration variable.
+    // We leverage this to find the linear iteration variable.
+    //
+    // TODO Add an attribute to `omp.loop_nest` that explicitly lists the
+    //      variables that correspond to the loop induction variables.
     BlockArgument arg = loopOp.getIVs().front();
     for (const Operation *user : arg.getUsers()) {
       if (auto storeOp = dyn_cast<LLVM::StoreOp>(user)) {
         for (Value linearVar : simdOp.getLinearVars()) {
           if (linearVar == storeOp.getAddr()) {
+            if (linearLoopIV)
+              return simdOp.emitError(
+                  "Could not determine the linear variable associated with the "
+                  "loop nest induction variable");
             linearLoopIV = linearVar;
-            break;
           }
         }
       }
     }
+    return success();
   }
 
   // Emit IR for updating Linear variables
@@ -4858,7 +4867,8 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder,
 
   // Initialize linear variables and linear step
   LinearClauseProcessor linearClauseProcessor;
-  linearClauseProcessor.initLinearIV(simdOp);
+  if (linearClauseProcessor.initLinearIV(simdOp).failed())
+    return failure();
 
   if (!simdOp.getLinearVars().empty()) {
     auto linearVarTypes = simdOp.getLinearVarTypes().value();
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
index 6eff5accca138..234f404ff5690 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
@@ -130,6 +130,21 @@ llvm.func @simd_linear_private(%lb : i32, %ub : i32, %step : i32, %i : !llvm.ptr
 
 // -----
 
+llvm.func @simd_linear_ambiguous_iv(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr, %y : !llvm.ptr) {
+  // expected-error @below {{Could not determine the linear variable associated with the loop nest induction variable}}
+  // expected-error @below {{LLVM Translation failed for operation: omp.simd}}
+  omp.simd linear(%x : !llvm.ptr = %step : i32, %y : !llvm.ptr = %step : i32) {
+    omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
+      llvm.store %iv, %x : i32, !llvm.ptr
+      llvm.store %iv, %y : i32, !llvm.ptr
+      omp.yield
+    }
+  } {linear_var_types = [i32, i32]}
+  llvm.return
+}
+
+// -----
+
 module attributes {llvm.target_triple = "amdgcn-amd-amdhsa", omp.is_target_device = true} {
   llvm.func @host_op_in_device(%arg0 : !llvm.ptr) {
     // expected-error @below {{unsupported host op found in device}}

>From 7d19047506073f01cb6135659cb64561812a3a88 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Fri, 24 Jul 2026 11:57:10 -0300
Subject: [PATCH 6/6] Allow repeated stores to the same location

---
 .../Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp  | 2 +-
 mlir/test/Target/LLVMIR/openmp-llvm.mlir                        | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index b938572fe3720..ca320ea0b1453 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -207,7 +207,7 @@ class LinearClauseProcessor {
       if (auto storeOp = dyn_cast<LLVM::StoreOp>(user)) {
         for (Value linearVar : simdOp.getLinearVars()) {
           if (linearVar == storeOp.getAddr()) {
-            if (linearLoopIV)
+            if (linearLoopIV && linearLoopIV != linearVar)
               return simdOp.emitError(
                   "Could not determine the linear variable associated with the "
                   "loop nest induction variable");
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
index 01fe0ae690955..de51468efdec1 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -768,6 +768,7 @@ llvm.func @simd_linear(%lb : i32, %ub : i32, %step : i32, %x : !llvm.ptr) {
 // CHECK-NEXT: store i32 %[[LOAD]], ptr %[[X]], align 4
   omp.simd linear(%x : !llvm.ptr = %step : i32) {
     omp.loop_nest (%iv) : i32 = (%lb) to (%ub) step (%step) {
+      llvm.store %iv, %x : i32, !llvm.ptr
       llvm.store %iv, %x : i32, !llvm.ptr
       omp.yield
     }



More information about the Mlir-commits mailing list