[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