[flang-commits] [flang] [flang][OpenMP] Diagnose task reduction array sections and elements (PR #215997)
via flang-commits
flang-commits at lists.llvm.org
Tue Aug 25 12:18:42 PDT 2026
================
@@ -183,6 +180,137 @@ static bool hasPrivatizedArrayElementReduction(
return false;
}
+static bool
+hasPartialArrayReductionObject(llvm::ArrayRef<Object> reductionObjects,
+ semantics::SemanticsContext &semaCtx) {
+ for (const Object &object : reductionObjects) {
+ if (!object.ref() || isWholeArraySection(object, semaCtx))
+ continue;
+ if (evaluate::IsArraySection(*object.ref()))
+ return true;
+ }
+ return false;
+}
+
+static void checkTaskModifierPartialArrayReduction(
+ mlir::Location loc, semantics::SemanticsContext &semaCtx,
+ mlir::omp::ReductionModifierAttr reductionMod,
+ llvm::ArrayRef<Object> reductionObjects) {
+ if (reductionMod &&
+ reductionMod.getValue() == mlir::omp::ReductionModifier::task &&
+ hasPartialArrayReductionObject(reductionObjects, semaCtx))
+ TODO(loc, "REDUCTION with TASK modifier of a partial array section");
+}
+
+static bool isArrayElementReductionObject(const Object &object) {
+ return object.ref() && object.ref()->Rank() == 0 &&
+ evaluate::IsArrayElement(*object.ref(), /*intoSubstring=*/false);
+}
+
+static bool
+hasArrayElementReductionObject(llvm::ArrayRef<Object> reductionObjects) {
+ return llvm::any_of(reductionObjects, isArrayElementReductionObject);
+}
+
+static bool isUserDefinedReductionOperator(
+ const clause::ReductionOperator &reductionOperator, const Object &object,
+ lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx) {
+ const semantics::Symbol *objectSymbol = object.sym();
+ const semantics::DeclTypeSpec *objectType =
+ objectSymbol ? objectSymbol->GetUltimate().GetType() : nullptr;
+ if (!objectType)
+ return false;
+
+ return common::visit(
+ common::visitors{
+ [&](const clause::DefinedOperator &definedOperator) {
+ return common::visit(
+ common::visitors{
+ [&](const clause::DefinedOperator::IntrinsicOperator &op) {
+ using IntrinsicOperator =
+ clause::DefinedOperator::IntrinsicOperator;
+ switch (op) {
+ case IntrinsicOperator::Add:
+ case IntrinsicOperator::Multiply:
+ case IntrinsicOperator::AND:
+ case IntrinsicOperator::OR:
+ case IntrinsicOperator::EQV:
+ case IntrinsicOperator::NEQV:
+ break;
+ default:
+ return false;
+ }
+
+ parser::CharBlock mangledName =
+ semantics::omp::MangledIntrinsicOperatorReductionName(
+ ReductionProcessor::toParserIntrinsicOperator(op),
+ semaCtx);
+ return semantics::omp::FindUserReductionSymbol(
+ converter.getCurrentScope(), mangledName,
+ objectType) != nullptr;
+ },
+ [&](const clause::DefinedOperator::DefinedOpName &op) {
+ const semantics::Symbol *operatorSymbol = op.v.sym();
+ return operatorSymbol &&
+ semantics::omp::FindOperatorUserReductionSymbol(
+ converter.getCurrentScope(), *operatorSymbol,
+ objectType);
+ },
+ },
+ definedOperator.u);
+ },
+ [&](const clause::ProcedureDesignator &procedureDesignator) {
+ const semantics::Symbol *symbol = procedureDesignator.v.sym();
+ return (symbol &&
+ symbol->GetUltimate()
+ .detailsIf<semantics::UserReductionDetails>()) ||
+ ReductionProcessor::findUserDefinedReductionForIntrinsic(
+ converter.getCurrentScope(), procedureDesignator,
+ objectType) != nullptr;
+ },
+ },
+ reductionOperator.u);
+}
+
+template <typename ReductionClause>
+static bool
+hasUserDefinedArrayElementReduction(const List<Clause> &clauses,
+ lower::AbstractConverter &converter,
+ semantics::SemanticsContext &semaCtx) {
+ bool found = false;
+ ClauseFinder::findRepeatableClause<ReductionClause>(
+ clauses,
+ [&](const ReductionClause &reductionClause, const parser::CharBlock &) {
+ if (found)
+ return;
+
+ const auto &reductionOperators =
+ std::get<clause::ReductionOperatorList>(reductionClause.t);
+ assert(reductionOperators.size() == 1 &&
+ "expected one reduction operator");
+ const ObjectList &objects = std::get<ObjectList>(reductionClause.t);
+ found = llvm::any_of(objects, [&](const Object &object) {
+ return isArrayElementReductionObject(object) &&
+ isUserDefinedReductionOperator(reductionOperators.front(),
+ object, converter, semaCtx);
+ });
+ });
+ return found;
+}
+
+static void checkTaskModifierUserDefinedArrayElementReduction(
+ mlir::Location loc, lower::AbstractConverter &converter,
+ semantics::SemanticsContext &semaCtx,
+ mlir::omp::ReductionModifierAttr reductionMod,
+ const List<Clause> &clauses) {
+ if (reductionMod &&
+ reductionMod.getValue() == mlir::omp::ReductionModifier::task &&
----------------
MattPD wrote:
Without the `task` modifier, a user-defined reduction on an array element covers the whole array:
```fortran
subroutine element(a)
integer :: a(4)
!$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
!$omp& initializer(omp_priv = 1)
!$omp parallel reduction(myred : a(2))
a(2) = a(2) + 1
!$omp end parallel
end
```
This compiles, and the generated reduction initializes and combines all four elements, although the clause names only `a(2)`. The `task`, `taskgroup`, and `taskloop` constructs already diagnose a user-defined reduction on an array element when the clause carries no modifier.
Could `parallel`, `do`, `sections`, and `scope` apply the same check without requiring the `task` modifier?
https://github.com/llvm/llvm-project/pull/215997
More information about the flang-commits
mailing list