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

via flang-commits flang-commits at lists.llvm.org
Fri Mar 20 12:17:34 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

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

Author: Leandro Lupori (luporl)

<details>
<summary>Changes</summary>

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

The previous regressions are fixed by #<!-- -->187097.


---

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


15 Files Affected:

- (modified) flang/include/flang/Semantics/openmp-utils.h (+1) 
- (modified) flang/lib/Lower/OpenMP/DataSharingProcessor.cpp (+139-50) 
- (modified) flang/lib/Lower/OpenMP/DataSharingProcessor.h (+9-4) 
- (modified) flang/lib/Lower/OpenMP/OpenMP.cpp (+4-1) 
- (modified) flang/lib/Semantics/openmp-utils.cpp (+22) 
- (modified) flang/lib/Semantics/resolve-directives.cpp (+2-23) 
- (modified) flang/test/Lower/OpenMP/composite_simd_linear.f90 (+8-7) 
- (modified) flang/test/Lower/OpenMP/distribute-parallel-do-simd.f90 (+6-7) 
- (modified) flang/test/Lower/OpenMP/distribute-simd.f90 (+3-3) 
- (added) flang/test/Lower/OpenMP/indirect-reference-privatization.f90 (+38) 
- (modified) flang/test/Lower/OpenMP/loop-pointer-variable.f90 (+5-5) 
- (modified) flang/test/Lower/OpenMP/ordered-simd.f90 (+2-2) 
- (modified) flang/test/Lower/OpenMP/parallel-lastprivate-clause-scalar.f90 (+43) 
- (removed) flang/test/Lower/OpenMP/privatize_predetermined_only_when_defined_by_eval.f90 (-35) 
- (modified) flang/test/Lower/OpenMP/wsloop-simd.f90 (+5-5) 


``````````diff
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..30ac20efa81a8 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);
 
@@ -235,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
@@ -263,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;
   };
 
@@ -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,69 @@ 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) &&
+        // 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())) {
       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 +597,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/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/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/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], om...
[truncated]

``````````

</details>


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


More information about the flang-commits mailing list