[flang-commits] [flang] Reland "[flang][OpenMP] Fix lowering of LINEAR iteration variables (#183794)" (PR #187766)

Leandro Lupori via flang-commits flang-commits at lists.llvm.org
Fri Mar 20 11:46:51 PDT 2026


https://github.com/luporl created https://github.com/llvm/llvm-project/pull/187766

Linear iteration variables were being treated as private. This fixes
one of the issues reported in #170784.

The previous regressions are fixed by #187097.


>From c6f80cd99a6bd012410907a2e00d6288af10bf5e Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Fri, 13 Mar 2026 16:24:11 -0300
Subject: [PATCH 1/2] [flang][OpenMP] Privatize indirectly referenced symbols

Symbols that may be referenced indirectly by lastprivate or linear
DSAs in nested constructs must not have their privatization skipped
in the enclosing context. This avoids updates to the wrong symbols.

This is needed to unblock PRs that fix #170784.

Fixes #179345
---
 flang/include/flang/Semantics/openmp-utils.h  |   1 +
 .../lib/Lower/OpenMP/DataSharingProcessor.cpp | 176 +++++++++++++-----
 flang/lib/Lower/OpenMP/DataSharingProcessor.h |  13 +-
 flang/lib/Semantics/openmp-utils.cpp          |  22 +++
 flang/lib/Semantics/resolve-directives.cpp    |  25 +--
 .../indirect-reference-privatization.f90      |  38 ++++
 .../parallel-lastprivate-clause-scalar.f90    |  43 +++++
 ...redetermined_only_when_defined_by_eval.f90 |  35 ----
 8 files changed, 246 insertions(+), 107 deletions(-)
 create mode 100644 flang/test/Lower/OpenMP/indirect-reference-privatization.f90
 delete mode 100644 flang/test/Lower/OpenMP/privatize_predetermined_only_when_defined_by_eval.f90

diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index cd599237e4182..9fd2401dfbc79 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -84,6 +84,7 @@ bool IsExtendedListItem(const Symbol &sym);
 bool IsVariableListItem(const Symbol &sym);
 bool IsTypeParamInquiry(const Symbol &sym);
 bool IsStructureComponent(const Symbol &sym);
+bool IsPrivatizable(const Symbol &sym);
 bool IsVarOrFunctionRef(const MaybeExpr &expr);
 
 bool IsWholeAssumedSizeArray(const parser::OmpObject &object);
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index fcf2ae9337295..074370576e3ca 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -27,6 +27,7 @@
 #include "flang/Parser/openmp-utils.h"
 #include "flang/Semantics/attr.h"
 #include "flang/Semantics/openmp-directive-sets.h"
+#include "flang/Semantics/openmp-utils.h"
 #include "flang/Semantics/tools.h"
 #include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/SmallSet.h"
@@ -96,6 +97,7 @@ void DataSharingProcessor::processStep1(
   collectDefaultSymbols();
   collectImplicitSymbols();
   collectPreDeterminedSymbols();
+  collectIndirectReferences();
 
   privatize(clauseOps, dir);
 
@@ -461,55 +463,39 @@ void DataSharingProcessor::collectSymbolsInNestedRegions(
   }
 }
 
-// Collect symbols to be default privatized in two steps.
-// In step 1, collect all symbols in `eval` that match `flag` into
-// `defaultSymbols`. In step 2, for nested constructs (if any), if and only if
-// the nested construct is an OpenMP construct, collect those nested
-// symbols skipping host associated symbols into `symbolsInNestedRegions`.
-// Later, in current context, all symbols in the set
-// `defaultSymbols` - `symbolsInNestedRegions` will be privatized.
-void DataSharingProcessor::collectSymbols(
-    semantics::Symbol::Flag flag,
-    llvm::SetVector<const semantics::Symbol *> &symbols) {
-  // Collect all scopes associated with 'eval'.
-  llvm::SetVector<const semantics::Scope *> clauseScopes;
-  std::function<void(const semantics::Scope *)> collectScopes =
+// Collect all scopes associated with `eval` and return the current scope.
+static const semantics::Scope *
+collectScopes(semantics::SemanticsContext &semaCtx,
+              lower::pft::Evaluation &eval,
+              llvm::SetVector<const semantics::Scope *> &clauseScopes) {
+  std::function<void(const semantics::Scope *)> collect =
       [&](const semantics::Scope *scope) {
         clauseScopes.insert(scope);
         for (const semantics::Scope &child : scope->children())
-          collectScopes(&child);
+          collect(&child);
       };
   parser::CharBlock source = getSource(semaCtx, eval);
   const semantics::Scope *curScope = nullptr;
   if (!source.empty()) {
     curScope = &semaCtx.FindScope(source);
-    collectScopes(curScope);
+    collect(curScope);
   }
-  // Collect all symbols referenced in the evaluation being processed,
-  // that matches 'flag'.
-  llvm::SetVector<const semantics::Symbol *> allSymbols;
-  converter.collectSymbolSet(eval, allSymbols, flag,
-                             /*collectSymbols=*/true,
-                             /*collectHostAssociatedSymbols=*/true);
-
-  llvm::SetVector<const semantics::Symbol *> symbolsInNestedRegions;
-  collectSymbolsInNestedRegions(eval, flag, symbolsInNestedRegions);
-
-  for (auto *symbol : allSymbols)
-    if (visitor.isSymbolDefineBy(symbol, eval))
-      symbolsInNestedRegions.remove(symbol);
+  return curScope;
+}
 
+void DataSharingProcessor::collectPrivatizedSymbols(
+    std::optional<semantics::Symbol::Flag> flag,
+    const llvm::SetVector<const semantics::Symbol *> &allSymbols,
+    const llvm::SetVector<const semantics::Symbol *> &symbolsInNestedRegions,
+    llvm::SetVector<const semantics::Symbol *> *symbols) {
   // Filter-out symbols that must not be privatized.
-  bool collectImplicit = flag == semantics::Symbol::Flag::OmpImplicit;
-  bool collectPreDetermined = flag == semantics::Symbol::Flag::OmpPreDetermined;
-
-  auto isPrivatizable = [](const semantics::Symbol &sym) -> bool {
-    return !semantics::IsProcedure(sym) &&
-           !sym.GetUltimate().has<semantics::DerivedTypeDetails>() &&
-           !sym.GetUltimate().has<semantics::NamelistDetails>() &&
-           !semantics::IsImpliedDoIndex(sym.GetUltimate()) &&
-           !semantics::IsStmtFunction(sym);
-  };
+  bool collectImplicit = false;
+  bool collectPreDetermined = false;
+  bool collectIndirectRefs = !flag.has_value();
+  if (!collectIndirectRefs) {
+    collectImplicit = *flag == semantics::Symbol::Flag::OmpImplicit;
+    collectPreDetermined = *flag == semantics::Symbol::Flag::OmpPreDetermined;
+  }
 
   auto shouldCollectSymbol = [&](const semantics::Symbol *sym) {
     if (collectImplicit) {
@@ -541,31 +527,66 @@ void DataSharingProcessor::collectSymbols(
              sym->test(semantics::Symbol::Flag::OmpPreDetermined);
     }
 
+    if (collectIndirectRefs)
+      return true;
+
     return !sym->test(semantics::Symbol::Flag::OmpImplicit) &&
            !sym->test(semantics::Symbol::Flag::OmpPreDetermined);
   };
 
+  llvm::SetVector<const semantics::Scope *> clauseScopes;
+  const semantics::Scope *curScope = collectScopes(semaCtx, eval, clauseScopes);
+
   for (const auto *sym : allSymbols) {
     assert(curScope && "couldn't find current scope");
-    if (isPrivatizable(*sym) && !symbolsInNestedRegions.contains(sym) &&
+    if (semantics::omp::IsPrivatizable(*sym) &&
+        !symbolsInNestedRegions.contains(sym) &&
         !explicitlyPrivatizedSymbols.contains(sym) &&
         shouldCollectSymbol(sym) && clauseScopes.contains(&sym->owner())) {
       allPrivatizedSymbols.insert(sym);
-      symbols.insert(sym);
+      if (symbols)
+        symbols->insert(sym);
     }
   }
 }
 
+// Collect symbols to be default privatized in two steps.
+// In step 1, collect all symbols in `eval` that match `flag` into
+// `defaultSymbols`. In step 2, for nested constructs (if any), if and only if
+// the nested construct is an OpenMP construct, collect those nested
+// symbols skipping host associated symbols into `symbolsInNestedRegions`.
+// Later, in current context, all symbols in the set
+// `defaultSymbols` - `symbolsInNestedRegions` will be privatized.
+void DataSharingProcessor::collectSymbols(
+    semantics::Symbol::Flag flag,
+    llvm::SetVector<const semantics::Symbol *> *symbols) {
+  // Collect all symbols referenced in the evaluation being processed,
+  // that matches 'flag'.
+  llvm::SetVector<const semantics::Symbol *> allSymbols;
+  converter.collectSymbolSet(eval, allSymbols, flag,
+                             /*collectSymbols=*/true,
+                             /*collectHostAssociatedSymbols=*/true);
+
+  llvm::SetVector<const semantics::Symbol *> symbolsInNestedRegions;
+  collectSymbolsInNestedRegions(eval, flag, symbolsInNestedRegions);
+
+  for (auto *symbol : allSymbols)
+    if (visitor.isSymbolDefineBy(symbol, eval))
+      symbolsInNestedRegions.remove(symbol);
+
+  collectPrivatizedSymbols(flag, allSymbols, symbolsInNestedRegions);
+}
+
 void DataSharingProcessor::collectDefaultSymbols() {
   using DataSharingAttribute = omp::clause::Default::DataSharingAttribute;
   for (const omp::Clause &clause : clauses) {
     if (const auto *defaultClause =
             std::get_if<omp::clause::Default>(&clause.u)) {
       if (defaultClause->v == DataSharingAttribute::Private)
-        collectSymbols(semantics::Symbol::Flag::OmpPrivate, defaultSymbols);
+        collectSymbols(semantics::Symbol::Flag::OmpPrivate, &defaultSymbols);
       else if (defaultClause->v == DataSharingAttribute::Firstprivate)
         collectSymbols(semantics::Symbol::Flag::OmpFirstPrivate,
-                       defaultSymbols);
+                       &defaultSymbols);
     }
   }
 }
@@ -573,13 +594,78 @@ void DataSharingProcessor::collectDefaultSymbols() {
 void DataSharingProcessor::collectImplicitSymbols() {
   // There will be no implicit symbols when a default clause is present.
   if (defaultSymbols.empty())
-    collectSymbols(semantics::Symbol::Flag::OmpImplicit, implicitSymbols);
+    collectSymbols(semantics::Symbol::Flag::OmpImplicit);
 }
 
 void DataSharingProcessor::collectPreDeterminedSymbols() {
   if (shouldCollectPreDeterminedSymbols)
-    collectSymbols(semantics::Symbol::Flag::OmpPreDetermined,
-                   preDeterminedSymbols);
+    collectSymbols(semantics::Symbol::Flag::OmpPreDetermined);
+}
+
+// Collect symbols that may be referenced indirectly by lastprivate or linear
+// DSAs in nested constructs. Their privatization must not be skipped in the
+// enclosing context, to avoid updating the wrong symbol.
+void DataSharingProcessor::collectIndirectReferences() {
+  // For compound constructs, collect the symbols only for the last leaf.
+  if (!shouldCollectPreDeterminedSymbols)
+    return;
+
+  llvm::SetVector<const semantics::Scope *> clauseScopes;
+  const semantics::Scope *curScope = collectScopes(semaCtx, eval, clauseScopes);
+  if (!curScope)
+    return;
+
+  // Collect all linear and lastprivate symbols.
+  llvm::SetVector<const semantics::Symbol *> allSymbols;
+  llvm::SetVector<const semantics::Symbol *> symbolsInNestedRegions;
+
+  auto collect = [&](semantics::Symbol::Flag flag) {
+    converter.collectSymbolSet(eval, allSymbols, flag,
+                               /*collectSymbols=*/true,
+                               /*collectHostAssociatedSymbols=*/true);
+    collectSymbolsInNestedRegions(eval, flag, symbolsInNestedRegions);
+  };
+  collect(semantics::Symbol::Flag::OmpLinear);
+  collect(semantics::Symbol::Flag::OmpLastPrivate);
+
+  for (auto *symbol : allSymbols)
+    if (visitor.isSymbolDefineBy(symbol, eval))
+      symbolsInNestedRegions.remove(symbol);
+
+  auto isPrivate = [](const semantics::Symbol &sym) {
+    using Symbol = semantics::Symbol;
+    Symbol::Flags privateFlags{
+        Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate,
+        Symbol::Flag::OmpLastPrivate, Symbol::Flag::OmpLinear};
+    return (sym.flags() & privateFlags).any();
+  };
+
+  // Find indirect references.
+  //
+  // A symbol in the current scope may be indirectly referenced by a DSA in
+  // nested constructs.
+  // To simplify the implementation, any linear/lastprivate symbol in a nested
+  // region is considered as an indirect reference. The produced output is
+  // correct, although it may contain privatizations that could be eliminated.
+  llvm::SetVector<const semantics::Symbol *> indirectReferences;
+  for (auto it = curScope->begin(), end = curScope->end(); it != end; ++it) {
+    const semantics::Symbol &sym = *it->second;
+    if (!isPrivate(sym))
+      continue;
+
+    for (const semantics::Symbol *nestedSym : symbolsInNestedRegions) {
+      if (&sym != nestedSym && sym.name() == nestedSym->name())
+        indirectReferences.insert(&sym);
+    }
+  }
+
+  // Remove indirectly referenced symbols from nested regions, to force them
+  // to be privatized.
+  for (const semantics::Symbol *sym : indirectReferences)
+    symbolsInNestedRegions.remove(sym);
+
+  collectPrivatizedSymbols(std::nullopt, indirectReferences,
+                           symbolsInNestedRegions);
 }
 
 void DataSharingProcessor::privatize(mlir::omp::PrivateClauseOps *clauseOps,
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.h b/flang/lib/Lower/OpenMP/DataSharingProcessor.h
index f6aa8652e3534..5dd564d4bbb61 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.h
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.h
@@ -96,8 +96,6 @@ class DataSharingProcessor {
   // Symbols in private, firstprivate, and/or lastprivate clauses.
   llvm::SetVector<const semantics::Symbol *> explicitlyPrivatizedSymbols;
   llvm::SetVector<const semantics::Symbol *> defaultSymbols;
-  llvm::SetVector<const semantics::Symbol *> implicitSymbols;
-  llvm::SetVector<const semantics::Symbol *> preDeterminedSymbols;
   llvm::SetVector<const semantics::Symbol *> allPrivatizedSymbols;
 
   lower::AbstractConverter &converter;
@@ -113,8 +111,14 @@ class DataSharingProcessor {
   OMPConstructSymbolVisitor visitor;
 
   bool needBarrier();
-  void collectSymbols(semantics::Symbol::Flag flag,
-                      llvm::SetVector<const semantics::Symbol *> &symbols);
+  void collectPrivatizedSymbols(
+      std::optional<semantics::Symbol::Flag> flag,
+      const llvm::SetVector<const semantics::Symbol *> &allSymbols,
+      const llvm::SetVector<const semantics::Symbol *> &symbolsInNestedRegions,
+      llvm::SetVector<const semantics::Symbol *> *symbols = nullptr);
+  void
+  collectSymbols(semantics::Symbol::Flag flag,
+                 llvm::SetVector<const semantics::Symbol *> *symbols = nullptr);
   void collectSymbolsInNestedRegions(
       lower::pft::Evaluation &eval, semantics::Symbol::Flag flag,
       llvm::SetVector<const semantics::Symbol *> &symbolsInNestedRegions);
@@ -126,6 +130,7 @@ class DataSharingProcessor {
   void collectDefaultSymbols();
   void collectImplicitSymbols();
   void collectPreDeterminedSymbols();
+  void collectIndirectReferences();
   void privatize(mlir::omp::PrivateClauseOps *clauseOps,
                  std::optional<llvm::omp::Directive> dir = std::nullopt);
   void copyLastPrivatize(mlir::Operation *op);
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 8abf008a72147..f8c6a259d4dd7 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -205,6 +205,28 @@ bool IsStructureComponent(const Symbol &sym) {
   return sym.owner().kind() == Scope::Kind::DerivedType;
 }
 
+bool IsPrivatizable(const Symbol &sym) {
+  auto *misc{sym.detailsIf<MiscDetails>()};
+  return IsVariableName(sym) && !IsProcedure(sym) && !IsStmtFunction(sym) &&
+      !IsNamedConstant(sym) &&
+      ( // OpenMP 5.2, 5.1.1: Assumed-size arrays are shared
+          !semantics::IsAssumedSizeArray(sym) ||
+          // If CrayPointer is among the DSA list then the
+          // CrayPointee is Privatizable
+          sym.test(Symbol::Flag::CrayPointee)) &&
+      !sym.owner().IsDerivedType() &&
+      sym.owner().kind() != Scope::Kind::ImpliedDos &&
+      sym.owner().kind() != Scope::Kind::Forall &&
+      !sym.detailsIf<semantics::AssocEntityDetails>() &&
+      !sym.detailsIf<semantics::NamelistDetails>() &&
+      (!misc ||
+          (misc->kind() != MiscDetails::Kind::ComplexPartRe &&
+              misc->kind() != MiscDetails::Kind::ComplexPartIm &&
+              misc->kind() != MiscDetails::Kind::KindParamInquiry &&
+              misc->kind() != MiscDetails::Kind::LenParamInquiry &&
+              misc->kind() != MiscDetails::Kind::ConstructName));
+}
+
 bool IsVarOrFunctionRef(const MaybeExpr &expr) {
   if (expr) {
     return evaluate::UnwrapProcedureRef(*expr) != nullptr ||
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 96f66246ac676..6bccaad15ad3e 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2583,27 +2583,6 @@ void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) {
   PopContext();
 }
 
-static bool IsPrivatizable(const Symbol *sym) {
-  auto *misc{sym->detailsIf<MiscDetails>()};
-  return IsVariableName(*sym) && !IsProcedure(*sym) && !IsNamedConstant(*sym) &&
-      ( // OpenMP 5.2, 5.1.1: Assumed-size arrays are shared
-          !semantics::IsAssumedSizeArray(*sym) ||
-          // If CrayPointer is among the DSA list then the
-          // CrayPointee is Privatizable
-          sym->test(Symbol::Flag::CrayPointee)) &&
-      !sym->owner().IsDerivedType() &&
-      sym->owner().kind() != Scope::Kind::ImpliedDos &&
-      sym->owner().kind() != Scope::Kind::Forall &&
-      !sym->detailsIf<semantics::AssocEntityDetails>() &&
-      !sym->detailsIf<semantics::NamelistDetails>() &&
-      (!misc ||
-          (misc->kind() != MiscDetails::Kind::ComplexPartRe &&
-              misc->kind() != MiscDetails::Kind::ComplexPartIm &&
-              misc->kind() != MiscDetails::Kind::KindParamInquiry &&
-              misc->kind() != MiscDetails::Kind::LenParamInquiry &&
-              misc->kind() != MiscDetails::Kind::ConstructName));
-}
-
 static bool IsTargetCaptureImplicitlyFirstprivatizeable(const Symbol &symbol,
     const Symbol::Flags &dsa, const Symbol::Flags &dataSharingAttributeFlags,
     const Symbol::Flags &dataMappingAttributeFlags,
@@ -2671,7 +2650,7 @@ static bool IsTargetCaptureImplicitlyFirstprivatizeable(const Symbol &symbol,
 
 void OmpAttributeVisitor::CreateImplicitSymbols(
     const parser::Name &name, const Symbol *symbol) {
-  if (!IsPrivatizable(symbol)) {
+  if (!omp::IsPrivatizable(*symbol)) {
     return;
   }
 
@@ -2947,7 +2926,7 @@ void OmpAttributeVisitor::Post(const parser::Name &name) {
   auto *symbol{name.symbol};
 
   if (symbol && WithinConstruct()) {
-    if (IsPrivatizable(symbol) && !IsObjectWithDSA(*symbol) &&
+    if (omp::IsPrivatizable(*symbol) && !IsObjectWithDSA(*symbol) &&
         !IsLocalInsideScope(*symbol, currScope())) {
       // TODO: create a separate function to go through the rules for
       //       predetermined, explicitly determined, and implicitly
diff --git a/flang/test/Lower/OpenMP/indirect-reference-privatization.f90 b/flang/test/Lower/OpenMP/indirect-reference-privatization.f90
new file mode 100644
index 0000000000000..fabc835554b29
--- /dev/null
+++ b/flang/test/Lower/OpenMP/indirect-reference-privatization.f90
@@ -0,0 +1,38 @@
+! RUN: %flang_fc1 -fopenmp -emit-hlfir %s -o - 2>&1 | FileCheck %s
+
+!CHECK-LABEL: func @_QPparallel_simd
+!CHECK: omp.parallel private(@_QFparallel_simdEk2_private_i32 {{.*}} -> %[[ARG:.*]] : !fir.ref<i32>)
+!CHECK:   %[[PRIV_K2:.*]]:2 = hlfir.declare %[[ARG]] {uniq_name = "_QFparallel_simdEk2"}
+!CHECK:   omp.simd linear(%[[PRIV_K2]]#0 {{.*}})
+
+subroutine parallel_simd
+  integer :: k1, k2
+  !$omp parallel default(none)
+    !$omp do
+    do k1 = 1, 10
+      do k2 = 1, 10
+      end do
+    end do
+    !$omp end do
+
+    !$omp simd linear(k2)
+    do k2 = 1, 10
+    end do
+    !$omp end simd
+  !$omp end parallel
+end subroutine parallel_simd
+
+!CHECK-LABEL: func @_QPtask_simd
+!CHECK: omp.task private(@_QFtask_simdEk_firstprivate_i32 %{{.*}})
+!CHECK:   %[[PRIV_K:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFtask_simdEk"}
+!CHECK:   omp.simd linear(%[[PRIV_K]]#0 : !fir.ref<i32> {{.*}})
+
+subroutine task_simd
+  integer :: k
+  !$omp task
+    !$omp simd linear(k)
+      do k = 1, 10
+      end do
+    !$omp end simd
+  !$omp end task
+end subroutine task_simd
diff --git a/flang/test/Lower/OpenMP/parallel-lastprivate-clause-scalar.f90 b/flang/test/Lower/OpenMP/parallel-lastprivate-clause-scalar.f90
index a6de35786cf64..72e03aedea2e0 100644
--- a/flang/test/Lower/OpenMP/parallel-lastprivate-clause-scalar.f90
+++ b/flang/test/Lower/OpenMP/parallel-lastprivate-clause-scalar.f90
@@ -260,3 +260,46 @@ subroutine firstpriv_lastpriv_int2(arg1)
 !$OMP END PARALLEL
 print *, arg1
 end subroutine
+
+! Check that LASTPRIVATE updates the private copy of `i` when used inside
+! nested PARALLEL constructs in which `i` is private.
+!CHECK-LABEL: func @_QPlastprivate_nested_parallel()
+!CHECK:         %[[I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFlastprivate_nested_parallelEi"} :
+!CHECK-SAME:                  (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK:         omp.parallel private(@_QFlastprivate_nested_parallelEi_private_i32 %[[I]]#0 {{.*}})
+!CHECK:           %[[PRIV_I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFlastprivate_nested_parallelEi"} :
+!CHECK-SAME:                         (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK:           omp.parallel {
+!CHECK:             omp.wsloop private(@_QFlastprivate_nested_parallelEi_private_i32 %[[PRIV_I]]#0 {{.*}})
+!CHECK:               hlfir.assign %{{.*}} to %[[PRIV_I]]#0
+
+subroutine lastprivate_nested_parallel()
+  integer :: i
+
+  !$OMP PARALLEL DEFAULT(PRIVATE)
+    !$OMP PARALLEL
+      !$OMP DO LASTPRIVATE(i)
+      do i = 1, 5
+      end do
+    !$OMP END PARALLEL
+  !$OMP END PARALLEL
+end subroutine
+
+!CHECK-LABEL: func @_QPlastprivate_nested_parallel2()
+!CHECK:         omp.parallel {
+!CHECK:           omp.wsloop private(@_QFlastprivate_nested_parallel2Ei_private_i32 {{.*}})
+!CHECK:             %[[PRIV_I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFlastprivate_nested_parallel2Ei"}
+!CHECK:             omp.parallel {
+!CHECK:               omp.wsloop private(@_QFlastprivate_nested_parallel2Ei_private_i32 %[[PRIV_I]]#0 {{.*}})
+!CHECK:                 hlfir.assign %{{.*}} to %[[PRIV_I]]#0
+
+subroutine lastprivate_nested_parallel2()
+  integer :: i, j, k
+
+  !$omp parallel do lastprivate(i)
+  do j = 1, 10
+    !$omp parallel do lastprivate(i)
+    do k = 2, 20
+    end do
+  end do
+end subroutine
diff --git a/flang/test/Lower/OpenMP/privatize_predetermined_only_when_defined_by_eval.f90 b/flang/test/Lower/OpenMP/privatize_predetermined_only_when_defined_by_eval.f90
deleted file mode 100644
index 5e9e622a45ef6..0000000000000
--- a/flang/test/Lower/OpenMP/privatize_predetermined_only_when_defined_by_eval.f90
+++ /dev/null
@@ -1,35 +0,0 @@
-! Fixes a regression uncovered by Fujitsu test 0686_0024.f90. In particular,
-! verifies that a pre-determined symbol is only privatized by its defining
-! evaluation (e.g. the loop for which the symbol was marked as pre-determined).
-
-! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
-
-subroutine privatize_predetermined_when_defined_by_eval
-  integer::i,ii
-  integer::j
-
-  !$omp parallel
-    !$omp do lastprivate(ii)
-    do i=1,10
-      do ii=1,10
-      enddo
-    enddo
-
-    !$omp do
-    do j=1,ii
-    enddo
-  !$omp end parallel
-end subroutine
-
-! Verify that nothing is privatized by the `omp.parallel` op.
-! CHECK: omp.parallel {
-
-! Verify that `i` and `ii` are privatized by the first loop.
-! CHECK:   omp.wsloop private(@{{.*}}ii_private_i32 %{{.*}}#0 -> %{{.*}}, @{{.*}}i_private_i32 %2#0 -> %{{.*}} : {{.*}}) {
-! CHECK:   }
-
-! Verify that `j` is privatized by the second loop.
-! CHECK:   omp.wsloop private(@{{.*}}j_private_i32 %{{.*}}#0 -> %{{.*}} : {{.*}}) {
-! CHECK:   }
-
-! CHECK: }

>From 0ac4d18d5a46fc8adc454d8359c0926fae009e75 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Fri, 20 Feb 2026 15:44:02 -0300
Subject: [PATCH 2/2] Reland "[flang][OpenMP] Fix lowering of LINEAR iteration
 variables (#183794)"

Linear iteration variables were being treated as private. This fixes
one of the issues reported in #170784.

The previous regressions are fixed by #187097.
---
 flang/lib/Lower/OpenMP/DataSharingProcessor.cpp   | 13 ++++++++-----
 flang/lib/Lower/OpenMP/OpenMP.cpp                 |  5 ++++-
 flang/test/Lower/OpenMP/composite_simd_linear.f90 | 15 ++++++++-------
 .../Lower/OpenMP/distribute-parallel-do-simd.f90  | 13 ++++++-------
 flang/test/Lower/OpenMP/distribute-simd.f90       |  6 +++---
 flang/test/Lower/OpenMP/loop-pointer-variable.f90 | 10 +++++-----
 flang/test/Lower/OpenMP/ordered-simd.f90          |  4 ++--
 flang/test/Lower/OpenMP/wsloop-simd.f90           | 10 +++++-----
 8 files changed, 41 insertions(+), 35 deletions(-)

diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index 074370576e3ca..30ac20efa81a8 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -237,11 +237,6 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
   // Such cases are suggested to be clearly documented and explained
   // instead of being silently skipped
   auto isException = [&](const Fortran::semantics::Symbol *sym) -> bool {
-    // `OmpPreDetermined` symbols cannot be exceptions since
-    // their privatized symbols are heavily used in FIR.
-    if (sym->test(Fortran::semantics::Symbol::Flag::OmpPreDetermined))
-      return false;
-
     // The handling of linear clause is deferred to the OpenMP
     // IRBuilder which is responsible for all its aspects,
     // including privatization. Privatizing linear variables at this point would
@@ -265,6 +260,11 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
     // draw a relation between %linear and %arg0. Hence skip.
     if (sym->test(Fortran::semantics::Symbol::Flag::OmpLinear))
       return true;
+
+    // `OmpPreDetermined` symbols cannot be exceptions since
+    // their privatized symbols are heavily used in FIR.
+    if (sym->test(Fortran::semantics::Symbol::Flag::OmpPreDetermined))
+      return false;
     return false;
   };
 
@@ -540,6 +540,9 @@ void DataSharingProcessor::collectPrivatizedSymbols(
   for (const auto *sym : allSymbols) {
     assert(curScope && "couldn't find current scope");
     if (semantics::omp::IsPrivatizable(*sym) &&
+        // Linear symbols are privatized by OpenMP IRBuilder. See comments
+        // in collectSymbolsForPrivatization() for more details.
+        !sym->test(semantics::Symbol::Flag::OmpLinear) &&
         !symbolsInNestedRegions.contains(sym) &&
         !explicitlyPrivatizedSymbols.contains(sym) &&
         shouldCollectSymbol(sym) && clauseScopes.contains(&sym->owner())) {
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index ae5f6f50bda09..77aced25f9f52 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -716,7 +716,10 @@ static mlir::Operation *
 createAndSetPrivatizedLoopVar(lower::AbstractConverter &converter,
                               mlir::Location loc, mlir::Value indexVal,
                               const semantics::Symbol *sym) {
-  assert(converter.isPresentShallowLookup(*sym) &&
+  // The handling of linear symbols is deferred to the OpenMP IRBuilder,
+  // which is responsible for all its aspects, including privatization.
+  assert((converter.isPresentShallowLookup(*sym) ||
+          sym->test(semantics::Symbol::Flag::OmpLinear)) &&
          "Expected symbol to be in symbol table.");
   return setLoopVar(converter, loc, indexVal, sym);
 }
diff --git a/flang/test/Lower/OpenMP/composite_simd_linear.f90 b/flang/test/Lower/OpenMP/composite_simd_linear.f90
index ff26f8663b35f..469b341c46657 100644
--- a/flang/test/Lower/OpenMP/composite_simd_linear.f90
+++ b/flang/test/Lower/OpenMP/composite_simd_linear.f90
@@ -8,7 +8,7 @@ subroutine do_simd
 !CHECK: %{{.*}} = arith.constant 1 : i32
 !CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32
 !CHECK: omp.wsloop {
-!CHECK: omp.simd linear(%[[X]]#0 : !fir.ref<i32> = %[[CONST]] : i32, %[[I]]#0 : !fir.ref<i32> = %[[IV_STEP]] : i32) private(@_QFdo_simdEi_private_i32 {{.*}} -> %arg0 : !fir.ref<i32>) {
+!CHECK: omp.simd linear(%[[X]]#0 : !fir.ref<i32> = %[[CONST]] : i32, %[[I]]#0 : !fir.ref<i32> = %[[IV_STEP]] : i32) {
 !CHECK: }
 !CHECK: } {linear_var_types = [i32, i32], omp.composite}
 !CHECK: } {omp.composite}
@@ -21,9 +21,10 @@ end subroutine do_simd
 
 
 subroutine distribute_simd
+!CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFdistribute_simdEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
 !CHECK: omp.teams {
-!CHECK: omp.distribute private(@_QFdistribute_simdEi_private_i32 {{.*}} -> %[[ARG0:.*]] : !fir.ref<i32>) {
-!CHECK: omp.simd linear(%[[ARG0]] : !fir.ref<i32> = %c1_i32 : i32) private(@_QFdistribute_simdEi_private_i32 %[[ARG0]] -> {{.*}} : !fir.ref<i32>) {
+!CHECK: omp.distribute {
+!CHECK: omp.simd linear(%[[I]]#0 : !fir.ref<i32> = %c1_i32 : i32) {
 !CHECK: } {linear_var_types = [i32], omp.composite}
 !CHECK: } {omp.composite}
     integer :: i
@@ -44,7 +45,7 @@ subroutine distribute_parallel_do
 !CHECK: %[[CONST]] = arith.constant 1 : i32
 !CHECK: omp.distribute {
 !CHECK: omp.wsloop {
-!CHECK: omp.simd linear(%[[I]]#0 : !fir.ref<i32> = %[[CONST]] : i32) private(@_QFdistribute_parallel_doEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) { 
+!CHECK: omp.simd linear(%[[I]]#0 : !fir.ref<i32> = %[[CONST]] : i32) {
     !$omp teams
     !$omp distribute parallel do simd linear(i:1)
     do i = 1, N
@@ -62,7 +63,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 : !fir.ref<i32> = %[[LINEAR_STEP]] : i32, %[[I]]#0 : !fir.ref<i32> = %[[IV_STEP]] : i32) private(@_QFparallel_doEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
+!CHECK: omp.simd linear(%[[X]]#0 : !fir.ref<i32> = %[[LINEAR_STEP]] : i32, %[[I]]#0 : !fir.ref<i32> = %[[IV_STEP]] : i32) {
     integer :: x
     !$omp parallel do simd linear(x:2)
     do i = 1, N
@@ -79,7 +80,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 : !fir.ref<i32> = %[[LINEAR_STEP]] : i32, %[[I]]#0 : !fir.ref<i32> = %[[IV_STEP]] : i32) private(@_QFteams_distributeEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
+!CHECK: omp.simd linear(%[[X]]#0 : !fir.ref<i32> = %[[LINEAR_STEP]] : i32, %[[I]]#0 : !fir.ref<i32> = %[[IV_STEP]] : i32) {
     integer :: x
     !$omp teams distribute simd linear(x)
     do i = 1, N
@@ -98,7 +99,7 @@ subroutine teams_distribute_parallel_do
 !CHECK: %[[IV_STEP:.*]] = arith.constant 1 : i32
 !CHECK: omp.distribute {
 !CHECK: omp.wsloop {
-!CHECK: omp.simd linear(%[[X]]#0 : !fir.ref<i32> = %c1_i32 : i32, %[[I]]#0 : !fir.ref<i32> = %c1_i32_1 : i32) private(@_QFteams_distribute_parallel_doEi_private_i32 %[[I]]#0 -> %arg0 : !fir.ref<i32>) {
+!CHECK: omp.simd linear(%[[X]]#0 : !fir.ref<i32> = %c1_i32 : i32, %[[I]]#0 : !fir.ref<i32> = %c1_i32_1 : i32) {
     integer :: x
     !$omp teams distribute parallel do simd linear(x)
     do i = 1, N
diff --git a/flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90 b/flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90
index 120177a0420bf..7f91177cc3311 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 linear({{.*}}) private({{.*}}) {
+  ! CHECK-NEXT: omp.simd linear({{.*}}) {
   ! 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 linear({{.*}}) private({{.*}}) {
+  ! CHECK-NEXT: omp.simd linear({{.*}}) {
   ! 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 linear({{.*}}) private({{.*}}) {
+  ! CHECK-NEXT: omp.simd linear({{.*}}) {
   ! 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 linear({{.*}}) simdlen(4) private({{.*}}) {
+  ! CHECK-NEXT: omp.simd linear({{.*}}) simdlen(4) {
   ! CHECK-NEXT: omp.loop_nest
   !$omp distribute parallel do simd simdlen(4)
   do index_ = 1, 10
@@ -86,11 +86,10 @@ subroutine distribute_parallel_do_simd_private()
   ! CHECK:      omp.parallel {
   ! CHECK:      omp.distribute {
   ! CHECK-NEXT: omp.wsloop {
-  ! 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.simd linear(%{{.*}}) private(@{{.*}} %[[X]]#0 -> %[[X_ARG:[^:]+]]
+  ! CHECK-SAME:                  : !fir.ref<i64>) {
   ! CHECK-NEXT: omp.loop_nest
   ! CHECK:      %[[X_PRIV:.*]]:2 = hlfir.declare %[[X_ARG]]
-  ! CHECK:      %[[INDEX_PRIV:.*]]:2 = hlfir.declare %[[INDEX_ARG]]
   !$omp distribute parallel do simd private(x)
   do index_ = 1, 10
   end do
diff --git a/flang/test/Lower/OpenMP/distribute-simd.f90 b/flang/test/Lower/OpenMP/distribute-simd.f90
index d0316d1a136ab..f06282a10d9f0 100644
--- a/flang/test/Lower/OpenMP/distribute-simd.f90
+++ b/flang/test/Lower/OpenMP/distribute-simd.f90
@@ -61,17 +61,17 @@ end subroutine distribute_simd_simdlen
 ! CHECK-LABEL: func.func @_QPdistribute_simd_private(
 subroutine distribute_simd_private()
   integer, allocatable :: tmp
+  ! CHECK: %[[INDEX:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFdistribute_simd_privateEindex_"}
   ! CHECK:      omp.teams
   !$omp teams
   ! CHECK:      omp.distribute
   ! CHECK:      omp.simd
-  ! CHECK-SAME: private(@[[PRIV_BOX_SYM:.*]] %{{.*}} -> %[[PRIV_BOX:.*]], @[[PRIV_IVAR_SYM:.*]] %{{.*}} -> %[[PRIV_IVAR:.*]] : !fir.ref<!fir.box<!fir.heap<i32>>>, !fir.ref<i32>)
+  ! CHECK-SAME: private(@[[PRIV_BOX_SYM:.*]] %{{.*}} -> %[[PRIV_BOX:.*]] : !fir.ref<!fir.box<!fir.heap<i32>>>)
   ! CHECK-NEXT: omp.loop_nest (%[[IVAR:.*]]) : i32
   !$omp distribute simd private(tmp)
   do index_ = 1, 10
   ! CHECK:      %[[PRIV_BOX_DECL:.*]]:2 = hlfir.declare %[[PRIV_BOX]]
-  ! CHECK:      %[[PRIV_IVAR_DECL:.*]]:2 = hlfir.declare %[[PRIV_IVAR]]
-  ! CHECK:      hlfir.assign %[[IVAR]] to %[[PRIV_IVAR_DECL]]#0
+  ! CHECK:      hlfir.assign %[[IVAR]] to %[[INDEX]]#0
   end do
   !$omp end distribute simd
   !$omp end teams
diff --git a/flang/test/Lower/OpenMP/loop-pointer-variable.f90 b/flang/test/Lower/OpenMP/loop-pointer-variable.f90
index 0ca5d3a197dc5..5a2ca05be7ccf 100644
--- a/flang/test/Lower/OpenMP/loop-pointer-variable.f90
+++ b/flang/test/Lower/OpenMP/loop-pointer-variable.f90
@@ -8,6 +8,7 @@ program loop_var
   integer, pointer :: ip1, ip2
   integer, allocatable :: ia1
 
+!CHECK:  %[[IA1:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFEia1"}
 !CHECK:  omp.wsloop private(@_QFEip1_private_box_ptr_i32 %{{.*}}#0 -> %[[IP1_PVT:.*]], @_QFEip2_private_box_ptr_i32 %{{.*}}#0 -> %[[IP2_PVT:.*]] : !fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
 !CHECK:  omp.loop_nest (%[[IP1_INDX:.*]], %[[IP2_INDX:.*]]) : i64 = ({{.*}}) to ({{.*}}) inclusive step ({{.*}})
 !CHECK:    %[[IP1_PVT_DECL:.*]]:2 = hlfir.declare %[[IP1_PVT]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFEip1"} : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
@@ -28,13 +29,12 @@ program loop_var
   end do
   !$omp end do
 
-!CHECK:    omp.simd private(@_QFEia1_private_box_heap_i32 %{{.*}}#0 -> %[[IA1_PVT:.*]] : !fir.ref<!fir.box<!fir.heap<i32>>>)
+!CHECK:    omp.simd
 !CHECK:      omp.loop_nest (%[[IA1_INDX:.*]]) : i64 = ({{.*}}) to ({{.*}}) inclusive step ({{.*}})
-!CHECK:        %[[IA1_PVT_DECL:.*]]:2 = hlfir.declare %[[IA1_PVT]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFEia1"} : (!fir.ref<!fir.box<!fir.heap<i32>>>) -> (!fir.ref<!fir.box<!fir.heap<i32>>>, !fir.ref<!fir.box<!fir.heap<i32>>>)
-!CHECK:        %[[IA1:.*]] = fir.convert %[[IA1_INDX]] : (i64) -> i32
-!CHECK:        %[[IA1_BOX:.*]] = fir.load %[[IA1_PVT_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<i32>>>
+!CHECK:        %[[IA1_INDX_I32:.*]] = fir.convert %[[IA1_INDX]] : (i64) -> i32
+!CHECK:        %[[IA1_BOX:.*]] = fir.load %[[IA1]]#0 : !fir.ref<!fir.box<!fir.heap<i32>>>
 !CHECK:        %[[IA1_ADDR:.*]] = fir.box_addr %[[IA1_BOX]] : (!fir.box<!fir.heap<i32>>) -> !fir.heap<i32>
-!CHECK:        hlfir.assign %[[IA1]] to %[[IA1_ADDR]] : i32, !fir.heap<i32>
+!CHECK:        hlfir.assign %[[IA1_INDX_I32]] to %[[IA1_ADDR]] : i32, !fir.heap<i32>
 !CHECK:        omp.yield
   !$omp simd
   do ia1 = 1, 10
diff --git a/flang/test/Lower/OpenMP/ordered-simd.f90 b/flang/test/Lower/OpenMP/ordered-simd.f90
index 849900993319a..5947c782414ca 100644
--- a/flang/test/Lower/OpenMP/ordered-simd.f90
+++ b/flang/test/Lower/OpenMP/ordered-simd.f90
@@ -7,7 +7,7 @@ subroutine ordered_simd(n)
   integer :: n, a(n), b(n), c(n), i
 
 ! CHECK-LABEL: func @_QPordered_simd
-! CHECK:         omp.simd linear({{.*}}) private({{.*}}) {
+! CHECK:         omp.simd linear({{.*}}) {
 ! CHECK:           omp.loop_nest (%{{.*}}) : i32 = (%{{.*}}) to (%{{.*}}) inclusive step (%{{.*}}) {
 ! CHECK:             omp.ordered.region par_level_simd {
 ! CHECK:               omp.terminator
@@ -34,7 +34,7 @@ subroutine ws_ordered_simd(n)
 
 ! CHECK-LABEL: func @_QPws_ordered_simd
 ! CHECK:         omp.wsloop ordered(0) {
-! CHECK:           omp.simd linear({{.*}}) private({{.*}}) {
+! CHECK:           omp.simd linear({{.*}}) {
 ! CHECK:             omp.loop_nest (%{{.*}}) : i32 = (%{{.*}}) to (%{{.*}}) inclusive step (%{{.*}}) {
 ! CHECK:               omp.ordered.region par_level_simd {
 ! CHECK:                 omp.terminator
diff --git a/flang/test/Lower/OpenMP/wsloop-simd.f90 b/flang/test/Lower/OpenMP/wsloop-simd.f90
index 03e35de04cace..541ec86c1b7e5 100644
--- a/flang/test/Lower/OpenMP/wsloop-simd.f90
+++ b/flang/test/Lower/OpenMP/wsloop-simd.f90
@@ -70,15 +70,15 @@ end subroutine do_simd_reduction
 ! CHECK-LABEL: func.func @_QPdo_simd_private(
 subroutine do_simd_private()
   integer, allocatable :: tmp
+  ! CHECK:      %[[I_DECL:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFdo_simd_privateEi"}
   ! CHECK:      omp.wsloop
   ! CHECK-NEXT: omp.simd
-  ! CHECK-SAME: private(@[[PRIV_BOX_SYM:.*]] %{{.*}} -> %[[PRIV_BOX:.*]], @[[PRIV_IVAR_SYM:.*]] %{{.*}} -> %[[PRIV_IVAR:.*]] : !fir.ref<!fir.box<!fir.heap<i32>>>, !fir.ref<i32>)
+  ! CHECK-SAME: private(@[[PRIV_BOX_SYM:.*]] %{{.*}} -> %[[PRIV_BOX:.*]] : !fir.ref<!fir.box<!fir.heap<i32>>>)
   ! CHECK-NEXT: omp.loop_nest (%[[IVAR:.*]]) : i32
   !$omp do simd private(tmp)
   do i=1, 10
   ! CHECK:      %[[PRIV_BOX_DECL:.*]]:2 = hlfir.declare %[[PRIV_BOX]]
-  ! CHECK:      %[[PRIV_IVAR_DECL:.*]]:2 = hlfir.declare %[[PRIV_IVAR]]
-  ! CHECK:      hlfir.assign %[[IVAR]] to %[[PRIV_IVAR_DECL]]#0
+  ! CHECK:      hlfir.assign %[[IVAR]] to %[[I_DECL]]#0
   ! CHECK:      %[[PRIV_BOX_LOAD:.*]] = fir.load %[[PRIV_BOX_DECL]]
   ! CHECK:      hlfir.assign %{{.*}} to %[[PRIV_BOX_DECL]]#0
   ! CHECK:      omp.yield
@@ -92,12 +92,12 @@ subroutine do_simd_lastprivate_firstprivate()
   ! CHECK:      omp.wsloop
   ! CHECK-SAME: private(@[[FIRSTPRIVATE_A_SYM:.*]] %{{.*}} -> %[[FIRSTPRIVATE_A:.*]] : !fir.ref<i32>)
   ! CHECK-NEXT: omp.simd
-  ! CHECK-SAME: private(@[[PRIVATE_A_SYM:.*]] %{{.*}} -> %[[PRIVATE_A:.*]], @[[PRIVATE_I_SYM:.*]] %{{.*}} -> %[[PRIVATE_I:.*]] : !fir.ref<i32>, !fir.ref<i32>)
+  ! CHECK-SAME: linear({{.*}}#0 : !fir.ref<i32> = %{{[^:]*}} : i32)
+  ! CHECK-SAME: private(@[[PRIVATE_A_SYM:.*]] %{{.*}} -> %[[PRIVATE_A:.*]] : !fir.ref<i32>)
   !$omp do simd lastprivate(a) firstprivate(a)
   do i = 1, 10
     ! CHECK: %[[FIRSTPRIVATE_A_DECL:.*]]:2 = hlfir.declare %[[FIRSTPRIVATE_A]]
     ! CHECK: %[[PRIVATE_A_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_A]]
-    ! CHECK: %[[PRIVATE_I_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_I]]
     a = a + 1
   end do
   !$omp end do simd



More information about the flang-commits mailing list