[flang] [llvm] [flang][OpenMP] Implement COMBINER clause (PR #172036)
Krzysztof Parzyszek via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 12 08:14:59 PST 2025
https://github.com/kparzysz created https://github.com/llvm/llvm-project/pull/172036
This adds parsing and lowering of the COMBINER clause. It utilizes the existing lowering code for combiner-expression to lower the COMBINER clause as well.
>From 101830e68e25bac61185d718c0d8d7b1155f54ab Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Mon, 3 Nov 2025 15:34:08 -0600
Subject: [PATCH] [flang][OpenMP] Implement COMBINER clause
This adds parsing and lowering of the COMBINER clause. It utilizes the
existing lowering code for combiner-expression to lower the COMBINER
clause as well.
---
flang/include/flang/Lower/OpenMP/Clauses.h | 6 +-
flang/include/flang/Parser/dump-parse-tree.h | 1 +
flang/include/flang/Parser/openmp-utils.h | 6 +-
flang/include/flang/Parser/parse-tree.h | 8 +
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 10 +-
flang/lib/Lower/OpenMP/Clauses.cpp | 49 +++---
flang/lib/Lower/OpenMP/OpenMP.cpp | 140 +++++++++---------
flang/lib/Parser/openmp-parsers.cpp | 25 ++--
flang/lib/Parser/openmp-utils.cpp | 16 +-
flang/lib/Semantics/check-omp-structure.cpp | 1 +
.../OpenMP/declare-reduction-combiner.f90 | 88 +++++++++++
llvm/include/llvm/Frontend/OpenMP/ClauseT.h | 31 ++--
llvm/include/llvm/Frontend/OpenMP/OMP.td | 4 +
13 files changed, 261 insertions(+), 124 deletions(-)
create mode 100644 flang/test/Parser/OpenMP/declare-reduction-combiner.f90
diff --git a/flang/include/flang/Lower/OpenMP/Clauses.h b/flang/include/flang/Lower/OpenMP/Clauses.h
index 455eda2738e6d..5f03877624be7 100644
--- a/flang/include/flang/Lower/OpenMP/Clauses.h
+++ b/flang/include/flang/Lower/OpenMP/Clauses.h
@@ -104,6 +104,7 @@ struct hash<Fortran::lower::omp::IdTy> {
namespace Fortran::lower::omp {
using Object = tomp::ObjectT<IdTy, ExprTy>;
using ObjectList = tomp::ObjectListT<IdTy, ExprTy>;
+using StylizedInstance = tomp::type::StylizedInstanceT<IdTy, ExprTy>;
Object makeObject(const parser::OmpObject &object,
semantics::SemanticsContext &semaCtx);
@@ -173,8 +174,10 @@ std::optional<ResultTy> maybeApplyToV(FuncTy &&func, const ArgTy *arg) {
std::optional<Object> getBaseObject(const Object &object,
semantics::SemanticsContext &semaCtx);
+StylizedInstance makeStylizedInstance(const parser::OmpStylizedInstance &inp,
+ semantics::SemanticsContext &semaCtx);
+
namespace clause {
-using StylizedInstance = tomp::type::StylizedInstanceT<IdTy, ExprTy>;
using Range = tomp::type::RangeT<ExprTy>;
using Mapper = tomp::type::MapperT<IdTy, ExprTy>;
using Iterator = tomp::type::IteratorT<TypeTy, IdTy, ExprTy>;
@@ -208,6 +211,7 @@ using Bind = tomp::clause::BindT<TypeTy, IdTy, ExprTy>;
using Capture = tomp::clause::CaptureT<TypeTy, IdTy, ExprTy>;
using Collapse = tomp::clause::CollapseT<TypeTy, IdTy, ExprTy>;
using Collector = tomp::clause::CollectorT<TypeTy, IdTy, ExprTy>;
+using Combiner = tomp::clause::CombinerT<TypeTy, IdTy, ExprTy>;
using Compare = tomp::clause::CompareT<TypeTy, IdTy, ExprTy>;
using Contains = tomp::clause::ContainsT<TypeTy, IdTy, ExprTy>;
using Copyin = tomp::clause::CopyinT<TypeTy, IdTy, ExprTy>;
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 252e156d2d459..dbc2b6541dd75 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -562,6 +562,7 @@ class ParseTreeDumper {
NODE(parser, OmpClauseList)
NODE(parser, OmpCloseModifier)
NODE_ENUM(OmpCloseModifier, Value)
+ NODE(parser, OmpCombinerClause)
NODE(parser, OmpCombinerExpression)
NODE(parser, OmpContainsClause)
NODE(parser, OmpContextSelectorSpecification)
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index 0fc7dbd29d6aa..bd200558e4c59 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -226,9 +226,9 @@ const BlockConstruct *GetFortranBlockConstruct(
const Block &GetInnermostExecPart(const Block &block);
bool IsStrictlyStructuredBlock(const Block &block);
-const OmpCombinerExpression *GetCombinerExpr(
- const OmpReductionSpecifier &rspec);
-const OmpInitializerExpression *GetInitializerExpr(const OmpClause &init);
+const OmpCombinerExpression *GetCombinerExpr(const OmpReductionSpecifier &x);
+const OmpCombinerExpression *GetCombinerExpr(const OmpClause &x);
+const OmpInitializerExpression *GetInitializerExpr(const OmpClause &x);
struct OmpAllocateInfo {
std::vector<const OmpAllocateDirective *> dirs;
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 93743709f10d2..b00d25373f801 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4395,6 +4395,14 @@ struct OmpCancellationConstructTypeClause {
std::tuple<OmpDirectiveName, std::optional<ScalarLogicalExpr>> t;
};
+// Ref: [6.0:262]
+//
+// combiner-clause -> // since 6.0
+// COMBINER(combiner-expr)
+struct OmpCombinerClause {
+ WRAPPER_CLASS_BOILERPLATE(OmpCombinerClause, OmpCombinerExpression);
+};
+
// Ref: [5.2:214]
//
// contains-clause ->
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 3c31b3a07f57f..b923e415231d6 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -390,10 +390,10 @@ bool ClauseProcessor::processInitializer(
mlir::Type type, mlir::Value ompOrig) {
lower::SymMapScope scope(symMap);
mlir::Value ompPrivVar;
- const clause::StylizedInstance &inst = clause->v.front();
+ const StylizedInstance &inst = clause->v.front();
for (const Object &object :
- std::get<clause::StylizedInstance::Variables>(inst.t)) {
+ std::get<StylizedInstance::Variables>(inst.t)) {
mlir::Value addr = builder.createTemporary(loc, ompOrig.getType());
fir::StoreOp::create(builder, loc, ompOrig, addr);
fir::FortranVariableFlagsEnum extraFlags = {};
@@ -412,7 +412,7 @@ bool ClauseProcessor::processInitializer(
// Lower the expression/function call
lower::StatementContext stmtCtx;
const semantics::SomeExpr &initExpr =
- std::get<clause::StylizedInstance::Instance>(inst.t);
+ std::get<StylizedInstance::Instance>(inst.t);
mlir::Value result = common::visit(
common::visitors{
[&](const evaluate::ProcedureRef &procRef) -> mlir::Value {
@@ -439,7 +439,9 @@ bool ClauseProcessor::processInitializer(
};
return true;
}
- return false;
+ TODO(converter.getCurrentLocation(),
+ "declare reduction without an initializer clause is not yet "
+ "supported");
}
bool ClauseProcessor::processMergeable(
diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp
index 9ea4e8fcd6c0e..d53054f005dea 100644
--- a/flang/lib/Lower/OpenMP/Clauses.cpp
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -197,6 +197,24 @@ std::optional<Object> getBaseObject(const Object &object,
return std::nullopt;
}
+StylizedInstance makeStylizedInstance(const parser::OmpStylizedInstance &inp,
+ semantics::SemanticsContext &semaCtx) {
+ ObjectList variables;
+ llvm::transform(std::get<std::list<parser::OmpStylizedDeclaration>>(inp.t),
+ std::back_inserter(variables),
+ [&](const parser::OmpStylizedDeclaration &s) {
+ return makeObject(s.var, semaCtx);
+ });
+
+ SomeExpr instance = [&]() {
+ if (auto &&expr = semantics::omp::MakeEvaluateExpr(inp))
+ return std::move(*expr);
+ llvm_unreachable("Expecting expression instance");
+ }();
+
+ return StylizedInstance{{std::move(variables), std::move(instance)}};
+}
+
// Helper macros
#define MAKE_EMPTY_CLASS(cls, from_cls) \
cls make(const parser::OmpClause::from_cls &, \
@@ -551,6 +569,17 @@ Collapse make(const parser::OmpClause::Collapse &inp,
return Collapse{/*N=*/makeExpr(inp.v, semaCtx)};
}
+Combiner make(const parser::OmpClause::Combiner &inp,
+ semantics::SemanticsContext &semaCtx) {
+ const parser::OmpCombinerExpression &cexpr = inp.v.v;
+ Combiner combiner;
+
+ for (const parser::OmpStylizedInstance &sinst : cexpr.v)
+ combiner.v.push_back(makeStylizedInstance(sinst, semaCtx));
+
+ return combiner;
+}
+
// Compare: empty
Contains make(const parser::OmpClause::Contains &inp,
@@ -988,24 +1017,8 @@ Initializer make(const parser::OmpClause::Initializer &inp,
const parser::OmpInitializerExpression &iexpr = inp.v.v;
Initializer initializer;
- for (const parser::OmpStylizedInstance &sinst : iexpr.v) {
- ObjectList variables;
- llvm::transform(
- std::get<std::list<parser::OmpStylizedDeclaration>>(sinst.t),
- std::back_inserter(variables),
- [&](const parser::OmpStylizedDeclaration &s) {
- return makeObject(s.var, semaCtx);
- });
-
- SomeExpr instance = [&]() {
- if (auto &&expr = semantics::omp::MakeEvaluateExpr(sinst))
- return std::move(*expr);
- llvm_unreachable("Expecting expression instance");
- }();
-
- initializer.v.push_back(
- StylizedInstance{{std::move(variables), std::move(instance)}});
- }
+ for (const parser::OmpStylizedInstance &sinst : iexpr.v)
+ initializer.v.push_back(makeStylizedInstance(sinst, semaCtx));
return initializer;
}
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 38ab42076f559..7965119764e5d 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -3602,57 +3602,28 @@ genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
TODO(converter.getCurrentLocation(), "OmpDeclareVariantDirective");
}
-static ReductionProcessor::GenCombinerCBTy
-processReductionCombiner(lower::AbstractConverter &converter,
- lower::SymMap &symTable,
- semantics::SemanticsContext &semaCtx,
- const parser::OmpReductionSpecifier &specifier) {
+static ReductionProcessor::GenCombinerCBTy processReductionCombiner(
+ lower::AbstractConverter &converter, lower::SymMap &symTable,
+ semantics::SemanticsContext &semaCtx, const clause::Combiner &combiner) {
ReductionProcessor::GenCombinerCBTy genCombinerCB;
- const auto &combinerExpression =
- std::get<std::optional<parser::OmpCombinerExpression>>(specifier.t)
- .value();
- const parser::OmpStylizedInstance &combinerInstance =
- combinerExpression.v.front();
- const parser::OmpStylizedInstance::Instance &instance =
- std::get<parser::OmpStylizedInstance::Instance>(combinerInstance.t);
-
- std::optional<semantics::SomeExpr> evalExprOpt;
- if (const auto *as = std::get_if<parser::AssignmentStmt>(&instance.u)) {
- auto &expr = std::get<parser::Expr>(as->t);
- evalExprOpt = makeExpr(expr, semaCtx);
- } else if (const auto *call = std::get_if<parser::CallStmt>(&instance.u)) {
- if (call->typedCall) {
- const auto &procRef = *call->typedCall;
- evalExprOpt = semantics::SomeExpr{procRef};
- } else {
- TODO(converter.getCurrentLocation(),
- "CallStmt without typedCall is not yet supported");
- }
- } else {
- TODO(converter.getCurrentLocation(), "Unsupported combiner instance type");
- }
-
- assert(evalExprOpt.has_value() && "evalExpr must be initialized");
- semantics::SomeExpr evalExpr = *evalExprOpt;
+ const StylizedInstance &inst = combiner.v.front();
+ semantics::SomeExpr evalExpr = std::get<StylizedInstance::Instance>(inst.t);
genCombinerCB = [&, evalExpr](fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Type type, mlir::Value lhs,
mlir::Value rhs, bool isByRef) {
lower::SymMapScope scope(symTable);
- const std::list<parser::OmpStylizedDeclaration> &declList =
- std::get<std::list<parser::OmpStylizedDeclaration>>(combinerInstance.t);
mlir::Value ompOutVar;
- for (const parser::OmpStylizedDeclaration &decl : declList) {
- auto &name = std::get<parser::ObjectName>(decl.var.t);
+ for (const Object &object : std::get<StylizedInstance::Variables>(inst.t)) {
mlir::Value addr = lhs;
mlir::Type type = lhs.getType();
- bool isRhs = name.ToString() == std::string("omp_in");
+ std::string name = object.sym()->name().ToString();
+ bool isRhs = name == "omp_in";
if (isRhs) {
addr = rhs;
type = rhs.getType();
}
- assert(name.symbol && "Reduction object name does not have a symbol");
if (!fir::conformsWithPassByRef(type)) {
addr = builder.createTemporary(loc, type);
fir::StoreOp::create(builder, loc, isRhs ? rhs : lhs, addr);
@@ -3660,13 +3631,13 @@ processReductionCombiner(lower::AbstractConverter &converter,
fir::FortranVariableFlagsEnum extraFlags = {};
fir::FortranVariableFlagsAttr attributes =
Fortran::lower::translateSymbolAttributes(builder.getContext(),
- *name.symbol, extraFlags);
+ *object.sym(), extraFlags);
auto declareOp =
- hlfir::DeclareOp::create(builder, loc, addr, name.ToString(), nullptr,
- {}, nullptr, nullptr, 0, attributes);
- if (name.ToString() == "omp_out")
+ hlfir::DeclareOp::create(builder, loc, addr, name, nullptr, {},
+ nullptr, nullptr, 0, attributes);
+ if (name == "omp_out")
ompOutVar = declareOp.getResult(0);
- symTable.addVariableDefinition(*name.symbol, declareOp);
+ symTable.addVariableDefinition(*object.sym(), declareOp);
}
lower::StatementContext stmtCtx;
@@ -3740,46 +3711,69 @@ getReductionType(lower::AbstractConverter &converter,
return reductionType;
}
-static void genOMP(
- lower::AbstractConverter &converter, lower::SymMap &symTable,
- semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,
- const parser::OpenMPDeclareReductionConstruct &declareReductionConstruct) {
+// Represent the reduction combiner as a clause, return reference to it.
+// If there is a "combiner" clause already present, do nothing. Otherwise
+// manufacture a combiner clause from the combiner expression on the reduction
+// specifier and append it to the list of clauses.
+static const clause::Combiner &
+appendCombiner(const parser::OpenMPDeclareReductionConstruct &construct,
+ List<Clause> &clauses, semantics::SemanticsContext &semaCtx) {
+ for (const Clause &clause : clauses) {
+ if (clause.id == llvm::omp::Clause::OMPC_combiner)
+ return std::get<clause::Combiner>(clause.u);
+ }
+
+ using namespace parser::omp;
+ const parser::OmpDirectiveSpecification &dirSpec = construct.v;
+ auto *specifier = GetFirstArgument<parser::OmpReductionSpecifier>(dirSpec);
+ assert(specifier && "Expecting reduction specifier");
+ if (auto *expr = GetCombinerExpr(*specifier)) {
+ clause::Combiner combiner;
+ for (const parser::OmpStylizedInstance &sinst : expr->v)
+ combiner.v.push_back(makeStylizedInstance(sinst, semaCtx));
+ clauses.push_back(makeClause(llvm::omp::Clause::OMPC_combiner,
+ std::move(combiner), expr->source));
+ return std::get<clause::Combiner>(clauses.back().u);
+ }
+
+ llvm_unreachable("Expecting reduction combiner");
+}
+
+static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
+ semantics::SemanticsContext &semaCtx,
+ lower::pft::Evaluation &eval,
+ const parser::OpenMPDeclareReductionConstruct &construct) {
if (semaCtx.langOptions().OpenMPSimd)
return;
- const parser::OmpArgumentList &args{declareReductionConstruct.v.Arguments()};
- const parser::OmpArgument &arg{args.v.front()};
- const auto &specifier = std::get<parser::OmpReductionSpecifier>(arg.u);
-
+ const auto &specifier =
+ DEREF(parser::omp::GetFirstArgument<parser::OmpReductionSpecifier>(
+ construct.v));
if (std::get<parser::OmpTypeNameList>(specifier.t).v.size() > 1)
TODO(converter.getCurrentLocation(),
"multiple types in declare reduction is not yet supported");
mlir::Type reductionType = getReductionType(converter, specifier);
+ List<Clause> clauses = makeClauses(construct.v.Clauses(), semaCtx);
+ const clause::Combiner &combiner =
+ appendCombiner(construct, clauses, semaCtx);
+
ReductionProcessor::GenCombinerCBTy genCombinerCB =
- processReductionCombiner(converter, symTable, semaCtx, specifier);
- const parser::OmpClauseList &initializer =
- declareReductionConstruct.v.Clauses();
- if (initializer.v.size() > 0) {
- List<Clause> clauses = makeClauses(initializer, semaCtx);
- ReductionProcessor::GenInitValueCBTy genInitValueCB;
- ClauseProcessor cp(converter, semaCtx, clauses);
- cp.processInitializer(symTable, genInitValueCB);
- const auto &identifier =
- std::get<parser::OmpReductionIdentifier>(specifier.t);
- const auto &designator =
- std::get<parser::ProcedureDesignator>(identifier.u);
- const auto &reductionName = std::get<parser::Name>(designator.u);
- bool isByRef = ReductionProcessor::doReductionByRef(reductionType);
- ReductionProcessor::createDeclareReductionHelper<
- mlir::omp::DeclareReductionOp>(
- converter, reductionName.ToString(), reductionType,
- converter.getCurrentLocation(), isByRef, genCombinerCB, genInitValueCB);
- } else {
- TODO(converter.getCurrentLocation(),
- "declare reduction without an initializer clause is not yet "
- "supported");
- }
+ processReductionCombiner(converter, symTable, semaCtx, combiner);
+
+ ReductionProcessor::GenInitValueCBTy genInitValueCB;
+ ClauseProcessor cp(converter, semaCtx, clauses);
+ cp.processInitializer(symTable, genInitValueCB);
+
+ const auto &identifier =
+ std::get<parser::OmpReductionIdentifier>(specifier.t);
+ const auto &designator = std::get<parser::ProcedureDesignator>(identifier.u);
+ const auto &reductionName = std::get<parser::Name>(designator.u);
+ bool isByRef = ReductionProcessor::doReductionByRef(reductionType);
+ ReductionProcessor::createDeclareReductionHelper<
+ mlir::omp::DeclareReductionOp>(
+ converter, reductionName.ToString(), reductionType,
+ converter.getCurrentLocation(), isByRef, genCombinerCB, genInitValueCB);
}
static void
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 24bdef9f88ed4..1f0d1be9adc00 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -488,27 +488,30 @@ static void InstantiateDeclareReduction(OmpDirectiveSpecification &spec) {
return;
}
- const OmpTypeNameList *typeNames{nullptr};
+ const OmpTypeNameList &typeNames{std::get<OmpTypeNameList>(rspec->t)};
if (auto *cexpr{
const_cast<OmpCombinerExpression *>(GetCombinerExpr(*rspec))}) {
- typeNames = &std::get<OmpTypeNameList>(rspec->t);
-
- InstantiateForTypes(*cexpr, *typeNames, OmpCombinerExpression::Variables());
+ InstantiateForTypes(*cexpr, typeNames, OmpCombinerExpression::Variables());
delete cexpr->state;
cexpr->state = nullptr;
- } else {
- // If there are no types, there is nothing else to do.
- return;
}
for (const OmpClause &clause : spec.Clauses().v) {
llvm::omp::Clause id{clause.Id()};
- if (id == llvm::omp::Clause::OMPC_initializer) {
+ if (id == llvm::omp::Clause::OMPC_combiner) {
+ if (auto *cexpr{
+ const_cast<OmpCombinerExpression *>(GetCombinerExpr(clause))}) {
+ InstantiateForTypes(
+ *cexpr, typeNames, OmpCombinerExpression::Variables());
+ delete cexpr->state;
+ cexpr->state = nullptr;
+ }
+ } else if (id == llvm::omp::Clause::OMPC_initializer) {
if (auto *iexpr{const_cast<OmpInitializerExpression *>(
GetInitializerExpr(clause))}) {
InstantiateForTypes(
- *iexpr, *typeNames, OmpInitializerExpression::Variables());
+ *iexpr, typeNames, OmpInitializerExpression::Variables());
delete iexpr->state;
iexpr->state = nullptr;
}
@@ -1316,6 +1319,8 @@ TYPE_PARSER(construct<OmpDetachClause>(Parser<OmpObject>{}))
TYPE_PARSER(construct<OmpHintClause>(scalarIntConstantExpr))
+TYPE_PARSER(construct<OmpCombinerClause>(Parser<OmpCombinerExpression>{}))
+
// init clause
TYPE_PARSER(construct<OmpInitClause>(
maybe(nonemptyList(Parser<OmpInitClause::Modifier>{}) / ":"),
@@ -1426,6 +1431,8 @@ TYPE_PARSER( //
"CAPTURE" >> construct<OmpClause>(construct<OmpClause::Capture>()) ||
"COLLAPSE" >> construct<OmpClause>(construct<OmpClause::Collapse>(
parenthesized(scalarIntConstantExpr))) ||
+ "COMBINER" >> construct<OmpClause>(construct<OmpClause::Combiner>(
+ parenthesized(Parser<OmpCombinerClause>{}))) ||
"COMPARE" >> construct<OmpClause>(construct<OmpClause::Compare>()) ||
"CONTAINS" >> construct<OmpClause>(construct<OmpClause::Contains>(
parenthesized(Parser<OmpContainsClause>{}))) ||
diff --git a/flang/lib/Parser/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp
index f96a5fca778e1..a9dbb55819b1e 100644
--- a/flang/lib/Parser/openmp-utils.cpp
+++ b/flang/lib/Parser/openmp-utils.cpp
@@ -173,13 +173,19 @@ bool IsStrictlyStructuredBlock(const Block &block) {
}
}
-const OmpCombinerExpression *GetCombinerExpr(
- const OmpReductionSpecifier &rspec) {
- return addr_if(std::get<std::optional<OmpCombinerExpression>>(rspec.t));
+const OmpCombinerExpression *GetCombinerExpr(const OmpReductionSpecifier &x) {
+ return addr_if(std::get<std::optional<OmpCombinerExpression>>(x.t));
}
-const OmpInitializerExpression *GetInitializerExpr(const OmpClause &init) {
- if (auto *wrapped{std::get_if<OmpClause::Initializer>(&init.u)}) {
+const OmpCombinerExpression *GetCombinerExpr(const OmpClause &x) {
+ if (auto *wrapped{std::get_if<OmpClause::Combiner>(&x.u)}) {
+ return &wrapped->v.v;
+ }
+ return nullptr;
+}
+
+const OmpInitializerExpression *GetInitializerExpr(const OmpClause &x) {
+ if (auto *wrapped{std::get_if<OmpClause::Initializer>(&x.u)}) {
return &wrapped->v.v;
}
return nullptr;
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index e640ca8c1d3d9..d3d6577aea83b 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5582,6 +5582,7 @@ CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args)
CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind)
CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture)
CHECK_SIMPLE_CLAUSE(Collector, OMPC_collector)
+CHECK_SIMPLE_CLAUSE(Combiner, OMPC_combiner)
CHECK_SIMPLE_CLAUSE(Compare, OMPC_compare)
CHECK_SIMPLE_CLAUSE(Contains, OMPC_contains)
CHECK_SIMPLE_CLAUSE(Default, OMPC_default)
diff --git a/flang/test/Parser/OpenMP/declare-reduction-combiner.f90 b/flang/test/Parser/OpenMP/declare-reduction-combiner.f90
new file mode 100644
index 0000000000000..291be6718847c
--- /dev/null
+++ b/flang/test/Parser/OpenMP/declare-reduction-combiner.f90
@@ -0,0 +1,88 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine f00
+ type t
+ integer :: x
+ end type
+
+ !$omp declare reduction(tred : t : omp_out%x = omp_out%x + omp_in%x)
+end
+
+!UNPARSE: SUBROUTINE f00
+!UNPARSE: TYPE :: t
+!UNPARSE: INTEGER :: x
+!UNPARSE: END TYPE
+!UNPARSE: !$OMP DECLARE_REDUCTION(tred:t: omp_out%x = omp_out%x + omp_in%x)
+!UNPARSE: END SUBROUTINE
+
+!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct
+!PARSE-TREE: OpenMPDeclareReductionConstruct -> OmpDirectiveSpecification
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare reduction
+!PARSE-TREE: | OmpArgumentList -> OmpArgument -> OmpReductionSpecifier
+!PARSE-TREE: | | OmpReductionIdentifier -> ProcedureDesignator -> Name = 'tred'
+!PARSE-TREE: | | OmpTypeNameList -> OmpTypeName -> TypeSpec -> DerivedTypeSpec
+!PARSE-TREE: | | | Name = 't'
+!PARSE-TREE: | | OmpCombinerExpression -> OmpStylizedInstance
+!PARSE-TREE: | | | OmpStylizedDeclaration
+!PARSE-TREE: | | | OmpStylizedDeclaration
+!PARSE-TREE: | | | Instance -> AssignmentStmt = 'omp_out%x=omp_out%x+omp_in%x'
+!PARSE-TREE: | | | | Variable = 'omp_out%x'
+!PARSE-TREE: | | | | | Designator -> DataRef -> StructureComponent
+!PARSE-TREE: | | | | | | DataRef -> Name = 'omp_out'
+!PARSE-TREE: | | | | | | Name = 'x'
+!PARSE-TREE: | | | | Expr = 'omp_out%x+omp_in%x'
+!PARSE-TREE: | | | | | Add
+!PARSE-TREE: | | | | | | Expr = 'omp_out%x'
+!PARSE-TREE: | | | | | | | Designator -> DataRef -> StructureComponent
+!PARSE-TREE: | | | | | | | | DataRef -> Name = 'omp_out'
+!PARSE-TREE: | | | | | | | | Name = 'x'
+!PARSE-TREE: | | | | | | Expr = 'omp_in%x'
+!PARSE-TREE: | | | | | | | Designator -> DataRef -> StructureComponent
+!PARSE-TREE: | | | | | | | | DataRef -> Name = 'omp_in'
+!PARSE-TREE: | | | | | | | | Name = 'x'
+!PARSE-TREE: | OmpClauseList ->
+!PARSE-TREE: | Flags = {}
+
+
+subroutine f01
+ type t
+ integer :: x
+ end type
+
+ !$omp declare reduction(tred : t) combiner(omp_out%x = omp_out%x + omp_in%x)
+end
+
+!UNPARSE: SUBROUTINE f01
+!UNPARSE: TYPE :: t
+!UNPARSE: INTEGER :: x
+!UNPARSE: END TYPE
+!UNPARSE: !$OMP DECLARE_REDUCTION(tred:t) COMBINER(omp_out%x = omp_out%x + omp_in%x)
+!UNPARSE: END SUBROUTINE
+
+
+!PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OpenMPDeclareReductionConstruct -> OmpDirectiveSpecification
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = declare reduction
+!PARSE-TREE: | OmpArgumentList -> OmpArgument -> OmpReductionSpecifier
+!PARSE-TREE: | | OmpReductionIdentifier -> ProcedureDesignator -> Name = 'tred'
+!PARSE-TREE: | | OmpTypeNameList -> OmpTypeName -> TypeSpec -> DerivedTypeSpec
+!PARSE-TREE: | | | Name = 't'
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Combiner -> OmpCombinerClause -> OmpCombinerExpression -> OmpStylizedInstance
+!PARSE-TREE: | | OmpStylizedDeclaration
+!PARSE-TREE: | | OmpStylizedDeclaration
+!PARSE-TREE: | | Instance -> AssignmentStmt = 'omp_out%x=omp_out%x+omp_in%x'
+!PARSE-TREE: | | | Variable = 'omp_out%x'
+!PARSE-TREE: | | | | Designator -> DataRef -> StructureComponent
+!PARSE-TREE: | | | | | DataRef -> Name = 'omp_out'
+!PARSE-TREE: | | | | | Name = 'x'
+!PARSE-TREE: | | | Expr = 'omp_out%x+omp_in%x'
+!PARSE-TREE: | | | | Add
+!PARSE-TREE: | | | | | Expr = 'omp_out%x'
+!PARSE-TREE: | | | | | | Designator -> DataRef -> StructureComponent
+!PARSE-TREE: | | | | | | | DataRef -> Name = 'omp_out'
+!PARSE-TREE: | | | | | | | Name = 'x'
+!PARSE-TREE: | | | | | Expr = 'omp_in%x'
+!PARSE-TREE: | | | | | | Designator -> DataRef -> StructureComponent
+!PARSE-TREE: | | | | | | | DataRef -> Name = 'omp_in'
+!PARSE-TREE: | | | | | | | Name = 'x'
+!PARSE-TREE: | Flags = {}
diff --git a/llvm/include/llvm/Frontend/OpenMP/ClauseT.h b/llvm/include/llvm/Frontend/OpenMP/ClauseT.h
index 0a0323559eb27..05ea2b84a7c11 100644
--- a/llvm/include/llvm/Frontend/OpenMP/ClauseT.h
+++ b/llvm/include/llvm/Frontend/OpenMP/ClauseT.h
@@ -459,6 +459,15 @@ struct CollectorT {
using IncompleteTrait = std::true_type;
};
+// [6.0:262]
+template <typename T, typename I, typename E> //
+struct CombinerT {
+ using List = ListT<type::StylizedInstanceT<I, E>>;
+ using WrapperTrait = std::true_type;
+ List v;
+};
+
+// V5.2: [15.8.3] `extended-atomic` clauses
template <typename T, typename I, typename E> //
struct CompareT {
using EmptyTrait = std::true_type;
@@ -1365,17 +1374,17 @@ template <typename T, typename I, typename E>
using WrapperClausesT = std::variant<
AbsentT<T, I, E>, AlignT<T, I, E>, AllocatorT<T, I, E>,
AtomicDefaultMemOrderT<T, I, E>, AtT<T, I, E>, BindT<T, I, E>,
- CollapseT<T, I, E>, ContainsT<T, I, E>, CopyinT<T, I, E>,
- CopyprivateT<T, I, E>, DefaultT<T, I, E>, DestroyT<T, I, E>,
- DetachT<T, I, E>, DeviceSafesyncT<T, I, E>, DeviceTypeT<T, I, E>,
- DynamicAllocatorsT<T, I, E>, EnterT<T, I, E>, ExclusiveT<T, I, E>,
- FailT<T, I, E>, FilterT<T, I, E>, FinalT<T, I, E>, FirstprivateT<T, I, E>,
- HasDeviceAddrT<T, I, E>, HintT<T, I, E>, HoldsT<T, I, E>,
- InclusiveT<T, I, E>, IndirectT<T, I, E>, InitializerT<T, I, E>,
- IsDevicePtrT<T, I, E>, LinkT<T, I, E>, MessageT<T, I, E>,
- NocontextT<T, I, E>, NontemporalT<T, I, E>, NovariantsT<T, I, E>,
- NumTeamsT<T, I, E>, NumThreadsT<T, I, E>, OrderedT<T, I, E>,
- PartialT<T, I, E>, PriorityT<T, I, E>, PrivateT<T, I, E>,
+ CollapseT<T, I, E>, CombinerT<T, I, E>, ContainsT<T, I, E>,
+ CopyinT<T, I, E>, CopyprivateT<T, I, E>, DefaultT<T, I, E>,
+ DestroyT<T, I, E>, DetachT<T, I, E>, DeviceSafesyncT<T, I, E>,
+ DeviceTypeT<T, I, E>, DynamicAllocatorsT<T, I, E>, EnterT<T, I, E>,
+ ExclusiveT<T, I, E>, FailT<T, I, E>, FilterT<T, I, E>, FinalT<T, I, E>,
+ FirstprivateT<T, I, E>, HasDeviceAddrT<T, I, E>, HintT<T, I, E>,
+ HoldsT<T, I, E>, InclusiveT<T, I, E>, IndirectT<T, I, E>,
+ InitializerT<T, I, E>, IsDevicePtrT<T, I, E>, LinkT<T, I, E>,
+ MessageT<T, I, E>, NocontextT<T, I, E>, NontemporalT<T, I, E>,
+ NovariantsT<T, I, E>, NumTeamsT<T, I, E>, NumThreadsT<T, I, E>,
+ OrderedT<T, I, E>, PartialT<T, I, E>, PriorityT<T, I, E>, PrivateT<T, I, E>,
ProcBindT<T, I, E>, ReverseOffloadT<T, I, E>, SafelenT<T, I, E>,
SelfMapsT<T, I, E>, SeverityT<T, I, E>, SharedT<T, I, E>, SimdlenT<T, I, E>,
SizesT<T, I, E>, PermutationT<T, I, E>, ThreadLimitT<T, I, E>,
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 81438f2e323c8..01d0b9af20bad 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -125,6 +125,9 @@ def OMPC_Collapse : Clause<[Spelling<"collapse">]> {
}
def OMPC_Collector : Clause<[Spelling<"collector">]> {
}
+def OMPC_Combiner : Clause<[Spelling<"combiner">]> {
+ let flangClass = "OmpCombinerClause";
+}
def OMPC_Compare : Clause<[Spelling<"compare">]> {
let clangClass = "OMPCompareClause";
}
@@ -775,6 +778,7 @@ def OMP_DeclareMapper : Directive<[Spelling<"declare mapper", 1, 52>,
def OMP_DeclareReduction : Directive<[Spelling<"declare reduction", 1, 52>,
Spelling<"declare_reduction", 60>]> {
let allowedOnceClauses = [
+ VersionedClause<OMPC_Combiner, 60>,
VersionedClause<OMPC_Initializer>,
];
let association = AS_None;
More information about the llvm-commits
mailing list