llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-flang-openmp @llvm/pr-subscribers-flang-fir-hlfir Author: Krzysztof Parzyszek (kparzysz) <details> <summary>Changes</summary> Update parsing of these two clauses and add semantic checks for them. Simplify some code in IsReductionAllowedForType and CheckReductionOperator. --- Patch is 36.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/118841.diff 16 Files Affected: - (modified) flang/include/flang/Parser/dump-parse-tree.h (+3) - (modified) flang/include/flang/Parser/parse-tree.h (+16-3) - (modified) flang/lib/Lower/OpenMP/Clauses.cpp (+16-12) - (modified) flang/lib/Parser/openmp-parsers.cpp (+15-4) - (modified) flang/lib/Parser/unparse.cpp (+8-3) - (modified) flang/lib/Semantics/check-omp-structure.cpp (+151-151) - (modified) flang/lib/Semantics/check-omp-structure.h (+6-5) - (modified) flang/test/Parser/OpenMP/in-reduction-clause.f90 (+6-6) - (modified) flang/test/Parser/OpenMP/reduction-modifier.f90 (+1-1) - (added) flang/test/Parser/OpenMP/task-reduction-clause.f90 (+23) - (modified) flang/test/Preprocessing/directive-contin-with-pp.F90 (+3-3) - (added) flang/test/Semantics/OpenMP/in-reduction.f90 (+44) - (modified) flang/test/Semantics/OpenMP/symbol08.f90 (+3-2) - (added) flang/test/Semantics/OpenMP/task-reduction.f90 (+44) - (modified) flang/test/Semantics/OpenMP/taskgroup01.f90 (+2) - (modified) llvm/include/llvm/Frontend/OpenMP/OMP.td (+1-1) ``````````diff diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index c6f35a07d81ea5..b87bc5b60cafe9 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -592,7 +592,10 @@ class ParseTreeDumper { NODE(parser, OmpReductionClause) NODE(OmpReductionClause, Modifier) NODE(parser, OmpInReductionClause) + NODE(OmpInReductionClause, Modifier) NODE(parser, OmpReductionCombiner) + NODE(parser, OmpTaskReductionClause) + NODE(OmpTaskReductionClause, Modifier) NODE(OmpReductionCombiner, FunctionCombiner) NODE(parser, OmpReductionInitializerClause) NODE(parser, OmpReductionIdentifier) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 8160b095f06dd9..e2530cea3652a2 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -3951,11 +3951,14 @@ struct OmpIfClause { std::tuple<MODIFIERS(), ScalarLogicalExpr> t; }; -// OMP 5.0 2.19.5.6 in_reduction-clause -> IN_REDUCTION (reduction-identifier: -// variable-name-list) +// Ref: [5.0:170-176], [5.1:197-205], [5.2:138-139] +// +// in-reduction-clause -> +// IN_REDUCTION(reduction-identifier: list) // since 5.0 struct OmpInReductionClause { TUPLE_CLASS_BOILERPLATE(OmpInReductionClause); - std::tuple<OmpReductionIdentifier, OmpObjectList> t; + MODIFIER_BOILERPLATE(OmpReductionIdentifier); + std::tuple<MODIFIERS(), OmpObjectList> t; }; // Ref: [4.5:199-201], [5.0:288-290], [5.1:321-322], [5.2:115-117] @@ -4070,6 +4073,16 @@ struct OmpScheduleClause { std::tuple<MODIFIERS(), Kind, std::optional<ScalarIntExpr>> t; }; +// Ref: [5.0:232-234], [5.1:264-266], [5.2:137] +// +// task-reduction-clause -> +// TASK_REDUCTION(reduction-identifier: list) // since 5.0 +struct OmpTaskReductionClause { + TUPLE_CLASS_BOILERPLATE(OmpTaskReductionClause); + MODIFIER_BOILERPLATE(OmpReductionIdentifier); + std::tuple<MODIFIERS(), OmpObjectList> t; +}; + // Ref: [4.5:107-109], [5.0:176-180], [5.1:205-210], [5.2:167-168] // // to-clause (in DECLARE TARGET) -> diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp index 10c31963ec493a..a0dc1be0afc5c0 100644 --- a/flang/lib/Lower/OpenMP/Clauses.cpp +++ b/flang/lib/Lower/OpenMP/Clauses.cpp @@ -859,10 +859,14 @@ Init make(const parser::OmpClause::Init &inp, InReduction make(const parser::OmpClause::InReduction &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpInReductionClause - auto &t0 = std::get<parser::OmpReductionIdentifier>(inp.v.t); + auto &mods = semantics::OmpGetModifiers(inp.v); + auto *m0 = + semantics::OmpGetUniqueModifier<parser::OmpReductionIdentifier>(mods); auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); + assert(m0 && "OmpReductionIdentifier is required"); + return InReduction{ - {/*ReductionIdentifiers=*/{makeReductionOperator(t0, semaCtx)}, + {/*ReductionIdentifiers=*/{makeReductionOperator(*m0, semaCtx)}, /*List=*/makeObjects(t1, semaCtx)}}; } @@ -1155,17 +1159,17 @@ Reduction make(const parser::OmpClause::Reduction &inp, ); auto &mods = semantics::OmpGetModifiers(inp.v); - auto *t0 = + auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpReductionModifier>(mods); - auto *t1 = + auto *m1 = semantics::OmpGetUniqueModifier<parser::OmpReductionIdentifier>(mods); - auto &t2 = std::get<parser::OmpObjectList>(inp.v.t); - assert(t1 && "OmpReductionIdentifier is required"); + auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); + assert(m1 && "OmpReductionIdentifier is required"); return Reduction{ - {/*ReductionModifier=*/maybeApplyToV(convert, t0), - /*ReductionIdentifiers=*/{makeReductionOperator(*t1, semaCtx)}, - /*List=*/makeObjects(t2, semaCtx)}}; + {/*ReductionModifier=*/maybeApplyToV(convert, m0), + /*ReductionIdentifiers=*/{makeReductionOperator(*m1, semaCtx)}, + /*List=*/makeObjects(t1, semaCtx)}}; } // Relaxed: empty @@ -1259,13 +1263,13 @@ TaskReduction make(const parser::OmpClause::TaskReduction &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpReductionClause auto &mods = semantics::OmpGetModifiers(inp.v); - auto *t0 = + auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpReductionIdentifier>(mods); auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); - assert(t0 && "OmpReductionIdentifier is required"); + assert(m0 && "OmpReductionIdentifier is required"); return TaskReduction{ - {/*ReductionIdentifiers=*/{makeReductionOperator(*t0, semaCtx)}, + {/*ReductionIdentifiers=*/{makeReductionOperator(*m0, semaCtx)}, /*List=*/makeObjects(t1, semaCtx)}}; } diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 86d475c1a15422..cff372658a996e 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -282,6 +282,9 @@ TYPE_PARSER(sourced( TYPE_PARSER(sourced(construct<OmpIfClause::Modifier>(OmpDirectiveNameParser{}))) +TYPE_PARSER(sourced(construct<OmpInReductionClause::Modifier>( + Parser<OmpReductionIdentifier>{}))) + TYPE_PARSER(sourced(construct<OmpLastprivateClause::Modifier>( Parser<OmpLastprivateModifier>{}))) @@ -306,6 +309,9 @@ TYPE_PARSER(sourced(construct<OmpScheduleClause::Modifier>(sourced( construct<OmpScheduleClause::Modifier>(Parser<OmpChunkModifier>{}) || construct<OmpScheduleClause::Modifier>(Parser<OmpOrderingModifier>{}))))) +TYPE_PARSER(sourced(construct<OmpTaskReductionClause::Modifier>( + Parser<OmpReductionIdentifier>{}))) + TYPE_PARSER(sourced(construct<OmpToClause::Modifier>( sourced(construct<OmpToClause::Modifier>(Parser<OmpExpectation>{}) || construct<OmpToClause::Modifier>(Parser<OmpMapper>{}) || @@ -407,7 +413,12 @@ TYPE_PARSER(construct<OmpReductionClause>( // OMP 5.0 2.19.5.6 IN_REDUCTION (reduction-identifier: variable-name-list) TYPE_PARSER(construct<OmpInReductionClause>( - Parser<OmpReductionIdentifier>{} / ":", Parser<OmpObjectList>{})) + maybe(nonemptyList(Parser<OmpInReductionClause::Modifier>{}) / ":"), + Parser<OmpObjectList>{})) + +TYPE_PARSER(construct<OmpTaskReductionClause>( + maybe(nonemptyList(Parser<OmpTaskReductionClause::Modifier>{}) / ":"), + Parser<OmpObjectList>{})) // OMP 5.0 2.11.4 allocate-clause -> ALLOCATE ([allocator:] variable-name-list) // OMP 5.2 2.13.4 allocate-clause -> ALLOCATE ([allocate-modifier @@ -609,15 +620,15 @@ TYPE_PARSER( parenthesized(Parser<OmpObjectList>{}))) || "PROC_BIND" >> construct<OmpClause>(construct<OmpClause::ProcBind>( parenthesized(Parser<OmpProcBindClause>{}))) || - "REDUCTION" >> construct<OmpClause>(construct<OmpClause::Reduction>( - parenthesized(Parser<OmpReductionClause>{}))) || + "REDUCTION"_id >> construct<OmpClause>(construct<OmpClause::Reduction>( + parenthesized(Parser<OmpReductionClause>{}))) || "IN_REDUCTION" >> construct<OmpClause>(construct<OmpClause::InReduction>( parenthesized(Parser<OmpInReductionClause>{}))) || "DETACH" >> construct<OmpClause>(construct<OmpClause::Detach>( parenthesized(Parser<OmpDetachClause>{}))) || "TASK_REDUCTION" >> construct<OmpClause>(construct<OmpClause::TaskReduction>( - parenthesized(Parser<OmpReductionClause>{}))) || + parenthesized(Parser<OmpTaskReductionClause>{}))) || "RELAXED" >> construct<OmpClause>(construct<OmpClause::Relaxed>()) || "RELEASE" >> construct<OmpClause>(construct<OmpClause::Release>()) || "REVERSE_OFFLOAD" >> diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index 4782cc1f2d7d7d..f8c4069e65f304 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2143,13 +2143,18 @@ class UnparseVisitor { } void Unparse(const OmpReductionClause &x) { using Modifier = OmpReductionClause::Modifier; - Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ":"); + Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": "); Walk(std::get<OmpObjectList>(x.t)); } void Unparse(const OmpDetachClause &x) { Walk(x.v); } void Unparse(const OmpInReductionClause &x) { - Walk(std::get<OmpReductionIdentifier>(x.t)); - Put(":"); + using Modifier = OmpInReductionClause::Modifier; + Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": "); + Walk(std::get<OmpObjectList>(x.t)); + } + void Unparse(const OmpTaskReductionClause &x) { + using Modifier = OmpTaskReductionClause::Modifier; + Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": "); Walk(std::get<OmpObjectList>(x.t)); } void Unparse(const OmpAllocateClause &x) { diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index 0d43bc907907a7..cb14d6032d345c 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -2821,7 +2821,6 @@ CHECK_SIMPLE_CLAUSE(Grainsize, OMPC_grainsize) CHECK_SIMPLE_CLAUSE(Hint, OMPC_hint) CHECK_SIMPLE_CLAUSE(Holds, OMPC_holds) CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive) -CHECK_SIMPLE_CLAUSE(InReduction, OMPC_in_reduction) CHECK_SIMPLE_CLAUSE(Match, OMPC_match) CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal) CHECK_SIMPLE_CLAUSE(NumTasks, OMPC_num_tasks) @@ -2846,7 +2845,6 @@ CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst) CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd) CHECK_SIMPLE_CLAUSE(Sizes, OMPC_sizes) CHECK_SIMPLE_CLAUSE(Permutation, OMPC_permutation) -CHECK_SIMPLE_CLAUSE(TaskReduction, OMPC_task_reduction) CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform) CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown) CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied) @@ -2915,14 +2913,17 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) { if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_reduction, GetContext().clauseSource, context_)) { - if (CheckReductionOperators(x)) { - CheckReductionTypeList(x); - } auto &modifiers{OmpGetModifiers(x.v)}; + const auto *ident{ + OmpGetUniqueModifier<parser::OmpReductionIdentifier>(modifiers)}; + assert(ident && "reduction-identifier is a required modifier"); + if (CheckReductionOperator(*ident, OmpGetModifierSource(modifiers, ident), + llvm::omp::OMPC_reduction)) { + CheckReductionObjectTypes(objects, *ident); + } using ReductionModifier = parser::OmpReductionModifier; - if (auto *maybeModifier{ - OmpGetUniqueModifier<ReductionModifier>(modifiers)}) { - CheckReductionModifier(*maybeModifier); + if (auto *modifier{OmpGetUniqueModifier<ReductionModifier>(modifiers)}) { + CheckReductionModifier(*modifier); } } CheckReductionObjects(objects, llvm::omp::Clause::OMPC_reduction); @@ -2934,70 +2935,88 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) { } } -bool OmpStructureChecker::CheckReductionOperators( - const parser::OmpClause::Reduction &x) { - bool ok = false; - auto &modifiers{OmpGetModifiers(x.v)}; - if (const auto *ident{ - OmpGetUniqueModifier<parser::OmpReductionIdentifier>(modifiers)}) { - - auto visitOperator{[&](const parser::DefinedOperator &dOpr) { - if (const auto *intrinsicOp{ - std::get_if<parser::DefinedOperator::IntrinsicOperator>( - &dOpr.u)}) { - ok = CheckIntrinsicOperator(*intrinsicOp); - } else { - context_.Say(GetContext().clauseSource, - "Invalid reduction operator in REDUCTION clause."_err_en_US, - ContextDirectiveAsFortran()); - } - }}; +void OmpStructureChecker::Enter(const parser::OmpClause::InReduction &x) { + CheckAllowedClause(llvm::omp::Clause::OMPC_in_reduction); + auto &objects{std::get<parser::OmpObjectList>(x.v.t)}; - auto visitDesignator{[&](const parser::ProcedureDesignator &procD) { - const parser::Name *name{std::get_if<parser::Name>(&procD.u)}; - if (name && name->symbol) { - const SourceName &realName{name->symbol->GetUltimate().name()}; - if (realName == "max" || realName == "min" || realName == "iand" || - realName == "ior" || realName == "ieor") { - ok = true; - } - } - if (!ok) { + if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_in_reduction, + GetContext().clauseSource, context_)) { + auto &modifiers{OmpGetModifiers(x.v)}; + const auto *ident{ + OmpGetUniqueModifier<parser::OmpReductionIdentifier>(modifiers)}; + assert(ident && "reduction-identifier is a required modifier"); + if (CheckReductionOperator(*ident, OmpGetModifierSource(modifiers, ident), + llvm::omp::OMPC_in_reduction)) { + CheckReductionObjectTypes(objects, *ident); + } + } + CheckReductionObjects(objects, llvm::omp::Clause::OMPC_in_reduction); +} + +void OmpStructureChecker::Enter(const parser::OmpClause::TaskReduction &x) { + CheckAllowedClause(llvm::omp::Clause::OMPC_task_reduction); + auto &objects{std::get<parser::OmpObjectList>(x.v.t)}; + + if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_task_reduction, + GetContext().clauseSource, context_)) { + auto &modifiers{OmpGetModifiers(x.v)}; + const auto *ident{ + OmpGetUniqueModifier<parser::OmpReductionIdentifier>(modifiers)}; + assert(ident && "reduction-identifier is a required modifier"); + if (CheckReductionOperator(*ident, OmpGetModifierSource(modifiers, ident), + llvm::omp::OMPC_task_reduction)) { + CheckReductionObjectTypes(objects, *ident); + } + } + CheckReductionObjects(objects, llvm::omp::Clause::OMPC_task_reduction); +} + +bool OmpStructureChecker::CheckReductionOperator( + const parser::OmpReductionIdentifier &ident, parser::CharBlock source, + llvm::omp::Clause clauseId) { + auto visitOperator{[&](const parser::DefinedOperator &dOpr) { + if (const auto *intrinsicOp{ + std::get_if<parser::DefinedOperator::IntrinsicOperator>(&dOpr.u)}) { + switch (*intrinsicOp) { + case parser::DefinedOperator::IntrinsicOperator::Add: + case parser::DefinedOperator::IntrinsicOperator::Multiply: + case parser::DefinedOperator::IntrinsicOperator::AND: + case parser::DefinedOperator::IntrinsicOperator::OR: + case parser::DefinedOperator::IntrinsicOperator::EQV: + case parser::DefinedOperator::IntrinsicOperator::NEQV: + return true; + case parser::DefinedOperator::IntrinsicOperator::Subtract: context_.Say(GetContext().clauseSource, - "Invalid reduction identifier in REDUCTION " - "clause."_err_en_US, + "The minus reduction operator is deprecated since OpenMP 5.2 and is not supported in the REDUCTION clause."_err_en_US, ContextDirectiveAsFortran()); + return false; + default: + break; } - }}; - common::visit(common::visitors{visitOperator, visitDesignator}, ident->u); - } - - return ok; -} + } + context_.Say(source, "Invalid reduction operator in %s clause."_err_en_US, + parser::ToUpperCaseLetters(getClauseName(clauseId).str())); + return false; + }}; -bool OmpStructureChecker::CheckIntrinsicOperator( - const parser::DefinedOperator::IntrinsicOperator &op) { + auto visitDesignator{[&](const parser::ProcedureDesignator &procD) { + const parser::Name *name{std::get_if<parser::Name>(&procD.u)}; + bool valid{false}; + if (name && name->symbol) { + const SourceName &realName{name->symbol->GetUltimate().name()}; + valid = + llvm::is_contained({"max", "min", "iand", "ior", "ieor"}, realName); + } + if (!valid) { + context_.Say(source, + "Invalid reduction identifier in %s clause."_err_en_US, + parser::ToUpperCaseLetters(getClauseName(clauseId).str())); + } + return valid; + }}; - switch (op) { - case parser::DefinedOperator::IntrinsicOperator::Add: - case parser::DefinedOperator::IntrinsicOperator::Multiply: - case parser::DefinedOperator::IntrinsicOperator::AND: - case parser::DefinedOperator::IntrinsicOperator::OR: - case parser::DefinedOperator::IntrinsicOperator::EQV: - case parser::DefinedOperator::IntrinsicOperator::NEQV: - return true; - case parser::DefinedOperator::IntrinsicOperator::Subtract: - context_.Say(GetContext().clauseSource, - "The minus reduction operator is deprecated since OpenMP 5.2 and is " - "not supported in the REDUCTION clause."_err_en_US, - ContextDirectiveAsFortran()); - break; - default: - context_.Say(GetContext().clauseSource, - "Invalid reduction operator in REDUCTION clause."_err_en_US, - ContextDirectiveAsFortran()); - } - return false; + return common::visit( + common::visitors{visitOperator, visitDesignator}, ident.u); } /// Check restrictions on objects that are common to all reduction clauses. @@ -3011,7 +3030,7 @@ void OmpStructureChecker::CheckReductionObjects( for (const parser::OmpObject &object : objects.v) { CheckIfContiguous(object); } - CheckReductionArraySection(objects); + CheckReductionArraySection(objects, clauseId); // An object must be definable. CheckDefinableObjects(symbols, clauseId); // Procedure pointers are not allowed. @@ -3064,100 +3083,82 @@ void OmpStructureChecker::CheckReductionObjects( } static bool IsReductionAllowedForType( - const parser::OmpClause::Reduction &x, const DeclTypeSpec &type) { - auto &modifiers{OmpGetModifiers(x.v)}; - const auto *definedOp{ - OmpGetUniqueModifier<parser::OmpReductionIdentifier>(modifiers)}; - if (!definedOp) { - return false; - } - // TODO: user defined reduction operators. Just allow everything for now. - bool ok{true}; - - auto IsLogical{[](const DeclTypeSpec &type) -> bool { + const parser::OmpReductionIdentifier &ident, const DeclTypeSpec &type) { + auto isLogical{[](const DeclTypeSpec &type) -> bool { return type.category() == DeclTypeSpec::Logical; }}; - auto IsCharacter{[](const DeclTypeSpec &type) -> bool { + auto isCharacter{[](const DeclTypeSpec &type) -> bool { return type.category() == DeclTypeSpec::Character; }}; - common::visit( - common::visitors{ - [&](const parser::DefinedOperator &dOpr) { - if (const auto *intrinsicOp{ - std::get_if<parser::DefinedOperator::IntrinsicOperator>( - &dOpr.u)}) { - // OMP5.2: The type [...] of a list item that appears in a - // reduction clause must be valid for the combiner expression - // See F2023: Table 10.2 - // .LT., .LE., .GT., .GE. are handled as procedure designators - // below. - switch (*intrinsicOp) { - case parser::DefinedOperator::IntrinsicOperator::Multiply: - [[fallthrough]]; - case parser::DefinedOperator::IntrinsicOperator::Add: - [[fallthrough]]; - case parser::DefinedOperator::IntrinsicOperator::Subtract: - ok = type.IsNumeric(TypeCategory::Integer) || - type.IsNumeric(TypeCategory::Real) || - type.IsNumeric(TypeCategory::Complex); - break; - - case parser::DefinedOperator::IntrinsicOperator::AND: - [[fallthrough]]; - case parser::DefinedOperator::IntrinsicOperator::OR: - [[fallthrough]]; - case parser::DefinedOperator::IntrinsicOperator::EQV: - [[fallthrough]]; - case parser::DefinedOperator::IntrinsicOperator::NEQV: - ok = IsLogical(type); - break; + auto checkOperator{[&](const parser::DefinedOperator &dOpr) { + if (const auto *intrinsicOp{ + std::get_if<parser::DefinedOperator::IntrinsicOperator>(&dOpr.u)}) { + // OMP5.2: The type [...] of a list item that appears in a + // reduction clause must be valid for the combiner expression + // See F2023: Table 10.2 + // .LT., .LE., .GT., .GE. are handled as procedure designators + // below. +... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/118841 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits