-
Notifications
You must be signed in to change notification settings - Fork 15k
[flang][OpenMP] Implement OpenMP stylized expressions #165049
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Consider OpenMP stylized expression to be a template to be instantiated with a series of types listed on the containing directive (currently DECLARE_REDUCTION). Create a series of instantiations in the parser, allowing OpenMP special variables to be declared separately for each type.
|
@llvm/pr-subscribers-flang-parser @llvm/pr-subscribers-flang-semantics Author: Krzysztof Parzyszek (kparzysz) ChangesConsider OpenMP stylized expression to be a template to be instantiated with a series of types listed on the containing directive (currently DECLARE_REDUCTION). Create a series of instantiations in the parser, allowing OpenMP special variables to be declared separately for each type. Patch is 73.19 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/165049.diff 24 Files Affected:
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index af8152deb8a52..002f67cbac6ca 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -596,7 +596,7 @@ class ParseTreeDumper {
NODE(parser, OmpInitClause)
NODE(OmpInitClause, Modifier)
NODE(parser, OmpInitializerClause)
- NODE(parser, OmpInitializerProc)
+ NODE(parser, OmpInitializerExpression)
NODE(parser, OmpInReductionClause)
NODE(OmpInReductionClause, Modifier)
NODE(parser, OmpInteropPreference)
@@ -674,6 +674,10 @@ class ParseTreeDumper {
NODE_ENUM(OmpSeverityClause, Severity)
NODE(parser, OmpStepComplexModifier)
NODE(parser, OmpStepSimpleModifier)
+ NODE(parser, OmpStylizedDeclaration)
+ NODE(parser, OmpStylizedExpression)
+ NODE(parser, OmpStylizedInstance)
+ NODE(OmpStylizedInstance, Instance)
NODE(parser, OmpTaskDependenceType)
NODE_ENUM(OmpTaskDependenceType, Value)
NODE(parser, OmpTaskReductionClause)
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index f761332c9cfd7..49db091af93a7 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -25,6 +25,13 @@
namespace Fortran::parser::omp {
+template <typename T> constexpr auto addr_if(std::optional<T> &x) {
+ return x ? &*x : nullptr;
+}
+template <typename T> constexpr auto addr_if(const std::optional<T> &x) {
+ return x ? &*x : nullptr;
+}
+
namespace detail {
using D = llvm::omp::Directive;
@@ -133,9 +140,24 @@ template <typename T> OmpDirectiveName GetOmpDirectiveName(const T &x) {
}
const OmpObjectList *GetOmpObjectList(const OmpClause &clause);
+
+template <typename T>
+const T *GetFirstArgument(const OmpDirectiveSpecification &spec) {
+ for (const OmpArgument &arg : spec.Arguments().v) {
+ if (auto *t{std::get_if<T>(&arg.u)}) {
+ return t;
+ }
+ }
+ return nullptr;
+}
+
const BlockConstruct *GetFortranBlockConstruct(
const ExecutionPartConstruct &epc);
+const OmpCombinerExpression *GetCombinerExpr(
+ const OmpReductionSpecifier &rspec);
+const OmpInitializerExpression *GetInitializerExpr(const OmpClause &init);
+
} // namespace Fortran::parser::omp
#endif // FORTRAN_PARSER_OPENMP_UTILS_H
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index be64ef3770c60..a3388a776cd32 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -24,7 +24,9 @@
#include "provenance.h"
#include "flang/Common/idioms.h"
#include "flang/Common/indirection.h"
+#include "flang/Common/reference.h"
#include "flang/Support/Fortran.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/Frontend/OpenACC/ACC.h.inc"
#include "llvm/Frontend/OpenMP/OMP.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
@@ -3504,6 +3506,8 @@ struct OmpDirectiveName {
// type-name list item
struct OmpTypeName {
+ CharBlock source;
+ mutable const semantics::DeclTypeSpec *declTypeSpec{nullptr};
UNION_CLASS_BOILERPLATE(OmpTypeName);
std::variant<TypeSpec, DeclarationTypeSpec> u;
};
@@ -3532,6 +3536,33 @@ struct OmpObjectList {
WRAPPER_CLASS_BOILERPLATE(OmpObjectList, std::list<OmpObject>);
};
+struct OmpStylizedDeclaration {
+ COPY_AND_ASSIGN_BOILERPLATE(OmpStylizedDeclaration);
+ // Since "Reference" isn't handled by parse-tree-visitor, add EmptyTrait,
+ // and visit the members by hand when needed.
+ using EmptyTrait = std::true_type;
+ common::Reference<const OmpTypeName> type;
+ EntityDecl var;
+};
+
+struct OmpStylizedInstance {
+ struct Instance {
+ UNION_CLASS_BOILERPLATE(Instance);
+ std::variant<AssignmentStmt, CallStmt, common::Indirection<Expr>> u;
+ };
+ TUPLE_CLASS_BOILERPLATE(OmpStylizedInstance);
+ std::tuple<std::list<OmpStylizedDeclaration>, Instance> t;
+};
+
+class ParseState;
+
+struct OmpStylizedExpression {
+ CharBlock source;
+ const ParseState *state{nullptr};
+ WRAPPER_CLASS_BOILERPLATE(
+ OmpStylizedExpression, std::list<OmpStylizedInstance>);
+};
+
// Ref: [4.5:201-207], [5.0:293-299], [5.1:325-331], [5.2:124]
//
// reduction-identifier ->
@@ -3549,9 +3580,22 @@ struct OmpReductionIdentifier {
// combiner-expression -> // since 4.5
// assignment-statement |
// function-reference
-struct OmpCombinerExpression {
- UNION_CLASS_BOILERPLATE(OmpCombinerExpression);
- std::variant<AssignmentStmt, FunctionReference> u;
+struct OmpCombinerExpression : public OmpStylizedExpression {
+ INHERITED_WRAPPER_CLASS_BOILERPLATE(
+ OmpCombinerExpression, OmpStylizedExpression);
+ static llvm::ArrayRef<CharBlock> Variables();
+};
+
+// Ref: [4.5:222:7-8], [5.0:305:28-29], [5.1:337:20-21], [5.2:127:6-8],
+// [6.0:242:3-5]
+//
+// initializer-expression -> // since 4.5
+// OMP_PRIV = expression |
+// subroutine-name(argument-list)
+struct OmpInitializerExpression : public OmpStylizedExpression {
+ INHERITED_WRAPPER_CLASS_BOILERPLATE(
+ OmpInitializerExpression, OmpStylizedExpression);
+ static llvm::ArrayRef<CharBlock> Variables();
};
inline namespace arguments {
@@ -4552,16 +4596,9 @@ struct OmpInReductionClause {
std::tuple<MODIFIERS(), OmpObjectList> t;
};
-// declare-reduction -> DECLARE REDUCTION (reduction-identifier : type-list
-// : combiner) [initializer-clause]
-struct OmpInitializerProc {
- TUPLE_CLASS_BOILERPLATE(OmpInitializerProc);
- std::tuple<ProcedureDesignator, std::list<ActualArgSpec>> t;
-};
// Initialization for declare reduction construct
struct OmpInitializerClause {
- UNION_CLASS_BOILERPLATE(OmpInitializerClause);
- std::variant<OmpInitializerProc, AssignmentStmt> u;
+ WRAPPER_CLASS_BOILERPLATE(OmpInitializerClause, OmpInitializerExpression);
};
// Ref: [4.5:199-201], [5.0:288-290], [5.1:321-322], [5.2:115-117]
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 04a063957082a..cb27d544ed9f5 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -830,6 +830,8 @@ class Symbol {
OmpUseDevicePtr, OmpUseDeviceAddr, OmpIsDevicePtr, OmpHasDeviceAddr,
// OpenMP data-copying attribute
OmpCopyIn, OmpCopyPrivate,
+ // OpenMP special variables
+ OmpInVar, OmpOrigVar, OmpOutVar, OmpPrivVar,
// OpenMP miscellaneous flags
OmpCommonBlock, OmpReduction, OmpInReduction, OmpAligned, OmpNontemporal,
OmpAllocate, OmpDeclarativeAllocateDirective,
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index d1e081cfd1b41..1f473660003a9 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -275,6 +275,13 @@ struct SpecificModifierParser {
// --- Iterator helpers -----------------------------------------------
+static EntityDecl MakeEntityDecl(ObjectName &&name) {
+ return EntityDecl(
+ /*ObjectName=*/std::move(name), std::optional<ArraySpec>{},
+ std::optional<CoarraySpec>{}, std::optional<CharLength>{},
+ std::optional<Initialization>{});
+}
+
// [5.0:47:17-18] In an iterator-specifier, if the iterator-type is not
// specified then the type of that iterator is default integer.
// [5.0:49:14] The iterator-type must be an integer type.
@@ -282,11 +289,7 @@ static std::list<EntityDecl> makeEntityList(std::list<ObjectName> &&names) {
std::list<EntityDecl> entities;
for (auto iter = names.begin(), end = names.end(); iter != end; ++iter) {
- EntityDecl entityDecl(
- /*ObjectName=*/std::move(*iter), std::optional<ArraySpec>{},
- std::optional<CoarraySpec>{}, std::optional<CharLength>{},
- std::optional<Initialization>{});
- entities.push_back(std::move(entityDecl));
+ entities.push_back(MakeEntityDecl(std::move(*iter)));
}
return entities;
}
@@ -306,6 +309,207 @@ static TypeDeclarationStmt makeIterSpecDecl(std::list<ObjectName> &&names) {
makeEntityList(std::move(names)));
}
+// --- Stylized expression handling -----------------------------------
+
+// OpenMP has a concept of am "OpenMP stylized expression". Syntactially
+// it looks like a typical Fortran expression (or statement), except:
+// - the only variables allowed in it are OpenMP special variables, the
+// exact set of these variables depends on the specific case of the
+// stylized expression
+// - the special OpenMP variables present may assume one or more types,
+// and the expression should be semantically valid for each type.
+//
+// The stylized expression can be thought of as a template, which will be
+// instantiated for each type provided somewhere in the context in which
+// the stylized expression appears.
+//
+// AST nodes:
+// - OmpStylizedExpression: contains the source string for the expression,
+// plus the list of instances (OmpStylizedInstance).
+// - OmpStylizedInstance: corresponds to the instantiation of the stylized
+// expression for a specific type. The way that the type is specified is
+// by creating declarations (OmpStylizedDeclaration) for the special
+// variables. Together with the AST tree corresponding to the stylized
+// expression the instantiation has enough information for semantic
+// analysis. Each instance has its own scope, and the special variables
+// have their own Symbol's (local to the scope).
+// - OmpStylizedDeclaration: encapsulates the information that the visitors
+// in resolve-names can use to "emulate" a declaration for a special
+// variable and allow name resolution in the instantiation AST to work.
+//
+// Implementation specifics:
+// The semantic analysis stores "evaluate::Expr" in each AST node rooted
+// in parser::Expr (in the typedExpr member). The evaluate::Expr is specific
+// to a given type, and so to allow different types for a given expression,
+// for each type a separate copy of the parsee::Expr subtree is created.
+// Normally, AST nodes are non-copyable (copy-ctor is deleted), so to create
+// several copies of a subtree, the same source string is parsed several
+// times. The ParseState member in OmpStylizedExpression is the parser state
+// immediately before the stylized expression.
+//
+// Initially, when OmpStylizedExpression is first created, the expression is
+// parsed as if it was an actual code, but this parsing is only done to
+// establish where the stylized expression ends (in the source). The source
+// and the initial parser state are store in the object, and the instance
+// list is empty.
+// Once the parsing of the containing OmpDirectiveSpecification completes,
+// a post-processing "parser" (OmpStylizedInstanceCreator) executes. This
+// post-processor examines the directive specification to see if it expects
+// any stylized expressions to be contained in it, and then instantiates
+// them for each such directive.
+
+template <typename A> struct NeverParser {
+ using resultType = A;
+ std::optional<resultType> Parse(ParseState &state) const {
+ // Always fail, but without any messages.
+ return std::nullopt;
+ }
+};
+
+template <typename A> constexpr auto never() { return NeverParser<A>{}; }
+
+template <typename A, typename B = void> struct NullParser;
+template <typename B> struct NullParser<std::optional<B>> {
+ using resultType = std::optional<B>;
+ std::optional<resultType> Parse(ParseState &) const {
+ return resultType{std::nullopt};
+ }
+};
+
+template <typename A> constexpr auto null() { return NullParser<A>{}; }
+
+// OmpStylizedDeclaration and OmpStylizedInstance are helper classes, and
+// don't correspond to anything in the source. Their parsers should still
+// exist, but they should never be executed.
+TYPE_PARSER(construct<OmpStylizedDeclaration>(never<OmpStylizedDeclaration>()))
+
+TYPE_PARSER( //
+ construct<OmpStylizedInstance::Instance>(Parser<AssignmentStmt>{}) ||
+ construct<OmpStylizedInstance::Instance>(
+ sourced(construct<CallStmt>(Parser<ProcedureDesignator>{},
+ null<std::optional<CallStmt::Chevrons>>(),
+ parenthesized(optionalList(actualArgSpec))))) ||
+ construct<OmpStylizedInstance::Instance>(indirect(expr)))
+
+TYPE_PARSER(construct<OmpStylizedInstance>(never<OmpStylizedInstance>()))
+
+struct OmpStylizedExpressionParser {
+ using resultType = OmpStylizedExpression;
+
+ std::optional<resultType> Parse(ParseState &state) const {
+ auto *saved{new ParseState(state)};
+ auto getSource{verbatim(Parser<OmpStylizedInstance::Instance>{} >> ok)};
+ if (auto &&ok{getSource.Parse(state)}) {
+ OmpStylizedExpression result{std::list<OmpStylizedInstance>{}};
+ result.source = ok->source;
+ result.state = saved;
+ // result.v remains empty
+ return std::move(result);
+ }
+ delete saved;
+ return std::nullopt;
+ }
+};
+
+static void Instantiate(OmpStylizedExpression &ose,
+ llvm::ArrayRef<const OmpTypeName *> types, llvm::ArrayRef<CharBlock> vars) {
+ // 1. For each var in the vars list, declare it with the corresponding
+ // type from types.
+ // 2. Run the parser to get the AST for the stylized expression.
+ // 3. Create OmpStylizedInstance and append it to the list in ose.
+ assert(types.size() == vars.size() && "List size mismatch");
+ ParseState state{DEREF(ose.state)};
+
+ std::list<OmpStylizedDeclaration> decls;
+ for (auto [type, var] : llvm::zip_equal(types, vars)) {
+ decls.emplace_back(OmpStylizedDeclaration{
+ common::Reference(*type), MakeEntityDecl(Name{var})});
+ }
+
+ if (auto &&instance{Parser<OmpStylizedInstance::Instance>{}.Parse(state)}) {
+ ose.v.emplace_back(
+ OmpStylizedInstance{std::move(decls), std::move(*instance)});
+ }
+}
+
+static void InstantiateForTypes(OmpStylizedExpression &ose,
+ const OmpTypeNameList &typeNames, llvm::ArrayRef<CharBlock> vars) {
+ // For each type in the type list, declare all variables in vars with
+ // that type, and complete the instantiation.
+ for (const OmpTypeName &t : typeNames.v) {
+ std::vector<const OmpTypeName *> types(vars.size(), &t);
+ Instantiate(ose, types, vars);
+ }
+}
+
+static void InstantiateDeclareReduction(OmpDirectiveSpecification &spec) {
+ // There can be arguments/clauses that don't make sense, that analysis
+ // is left until semantic checks. Tolerate any unexpected stuff.
+ auto *rspec{GetFirstArgument<OmpReductionSpecifier>(spec)};
+ if (!rspec) {
+ return;
+ }
+
+ const OmpTypeNameList *typeNames{nullptr};
+
+ if (auto *cexpr{GetCombinerExpr(*rspec)}) {
+ typeNames = &std::get<OmpTypeNameList>(rspec->t);
+
+ InstantiateForTypes(const_cast<OmpCombinerExpression &>(*cexpr), *typeNames,
+ OmpCombinerExpression::Variables());
+ delete cexpr->state;
+ } 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 (auto *iexpr{GetInitializerExpr(clause)}) {
+ InstantiateForTypes(const_cast<OmpInitializerExpression &>(*iexpr),
+ *typeNames, OmpInitializerExpression::Variables());
+ delete iexpr->state;
+ }
+ }
+ }
+}
+
+static void InstantiateStylizedDirective(OmpDirectiveSpecification &spec) {
+ const OmpDirectiveName &dirName{spec.DirName()};
+ if (dirName.v == llvm::omp::Directive::OMPD_declare_reduction) {
+ InstantiateDeclareReduction(spec);
+ }
+}
+
+template <typename P,
+ typename = std::enable_if_t<
+ std::is_same_v<typename P::resultType, OmpDirectiveSpecification>>>
+struct OmpStylizedInstanceCreator {
+ using resultType = OmpDirectiveSpecification;
+ constexpr OmpStylizedInstanceCreator(P p) : parser_(p) {}
+
+ std::optional<resultType> Parse(ParseState &state) const {
+ if (auto &&spec{parser_.Parse(state)}) {
+ InstantiateStylizedDirective(*spec);
+ return std::move(spec);
+ }
+ return std::nullopt;
+ }
+
+private:
+ const P parser_;
+};
+
+template <typename P>
+OmpStylizedInstanceCreator(P) -> OmpStylizedInstanceCreator<P>;
+
+// --- Parsers for types ----------------------------------------------
+
+TYPE_PARSER( //
+ sourced(construct<OmpTypeName>(Parser<DeclarationTypeSpec>{})) ||
+ sourced(construct<OmpTypeName>(Parser<TypeSpec>{})))
+
// --- Parsers for arguments ------------------------------------------
// At the moment these are only directive arguments. This is needed for
@@ -366,10 +570,6 @@ struct OmpArgumentListParser {
}
};
-TYPE_PARSER( //
- construct<OmpTypeName>(Parser<DeclarationTypeSpec>{}) ||
- construct<OmpTypeName>(Parser<TypeSpec>{}))
-
// 2.15.3.6 REDUCTION (reduction-identifier: variable-name-list)
TYPE_PARSER(construct<OmpReductionIdentifier>(Parser<DefinedOperator>{}) ||
construct<OmpReductionIdentifier>(Parser<ProcedureDesignator>{}))
@@ -1065,7 +1265,8 @@ TYPE_PARSER(construct<OmpOtherwiseClause>(
TYPE_PARSER(construct<OmpWhenClause>(
maybe(nonemptyList(Parser<OmpWhenClause::Modifier>{}) / ":"),
- maybe(indirect(Parser<OmpDirectiveSpecification>{}))))
+ maybe(indirect(
+ OmpStylizedInstanceCreator(Parser<OmpDirectiveSpecification>{})))))
// OMP 5.2 12.6.1 grainsize([ prescriptiveness :] scalar-integer-expression)
TYPE_PARSER(construct<OmpGrainsizeClause>(
@@ -1777,12 +1978,7 @@ TYPE_PARSER(
Parser<OpenMPInteropConstruct>{})) /
endOfLine)
-TYPE_PARSER(construct<OmpInitializerProc>(Parser<ProcedureDesignator>{},
- parenthesized(many(maybe(","_tok) >> Parser<ActualArgSpec>{}))))
-
-TYPE_PARSER(construct<OmpInitializerClause>(
- construct<OmpInitializerClause>(assignmentStmt) ||
- construct<OmpInitializerClause>(Parser<OmpInitializerProc>{})))
+TYPE_PARSER(construct<OmpInitializerClause>(Parser<OmpInitializerExpression>{}))
// OpenMP 5.2: 7.5.4 Declare Variant directive
TYPE_PARSER(sourced(construct<OmpDeclareVariantDirective>(
@@ -1794,7 +1990,7 @@ TYPE_PARSER(sourced(construct<OmpDeclareVariantDirective>(
TYPE_PARSER(sourced(construct<OpenMPDeclareReductionConstruct>(
predicated(Parser<OmpDirectiveName>{},
IsDirective(llvm::omp::Directive::OMPD_declare_reduction)) >=
- Parser<OmpDirectiveSpecification>{})))
+ OmpStylizedInstanceCreator(Parser<OmpDirectiveSpecification>{}))))
// 2.10.6 Declare Target Construct
TYPE_PARSER(sourced(construct<OpenMPDeclareTargetConstruct>(
@@ -1832,8 +2028,8 @@ TYPE_PARSER(sourced(construct<OpenMPDeclareMapperConstruct>(
IsDirective(llvm::omp::Directive::OMPD_declare_mapper)) >=
Parser<OmpDirectiveSpecification>{})))
-TYPE_PARSER(construct<OmpCombinerExpression>(Parser<AssignmentStmt>{}) ||
- construct<OmpCombinerExpression>(Parser<FunctionReference>{}))
+TYPE_PARSER(construct<OmpCombinerExpression>(OmpStylizedExpressionParser{}))
+TYPE_PARSER(construct<OmpInitializerExpression>(OmpStylizedExpressionParser{}))
TYPE_PARSER(sourced(construct<OpenMPCriticalConstruct>(
OmpBlockConstructParser{llvm::omp::Directive::OMPD_critical})))
diff --git a/flang/lib/Parser/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp
index 937a17f29f221..95ad3f60770f5 100644
--- a/flang/lib/Parser/openmp-utils.cpp
+++ b/flang/lib/Parser/openmp-utils.cpp
@@ -74,4 +74,16 @@ const BlockConstruct *GetFortranBlockConstruct(
return nullptr;
}
+const OmpCombinerExpression *GetCombinerExpr(
+ const OmpReductionSpecifier &rspec) {
+ return addr_if(std::get<std::optional<OmpCombinerExpression>>(rspec.t));
+}
+
+const OmpInitializerExpression *GetInitializerExpr(const OmpClause &init) {
+ if (auto *wrapped{std::get_if<OmpClause::Initializer>(&init.u)}) {
+ return &wrapped->v.v;
+ }
+ return nullptr;
+}
+
} // namespace Fortran::parser::omp
diff --git a/flang/lib/Parser/parse-tree.cpp b/flang/lib/Parser/parse-tree.cpp
index 8cbaa399c4763..ad0016e1404f9 100644
--- a/flang/lib/Parser/parse-tree.cpp
+++ b/flang/lib/Parser/parse-tree.cpp
@@ -11,6 +11,7 @@
#include "flang/Common/indirection.h"
#include "flang/Parser/tools.h"
#include "flang/Parser/user-state.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/Frontend/OpenMP/OMP.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -430,4 +431,30 @@ const OmpClauseList &OmpDirectiveSpecification::Clauses() const {
...
[truncated]
|
|
@llvm/pr-subscribers-flang-openmp Author: Krzysztof Parzyszek (kparzysz) ChangesConsider OpenMP stylized expression to be a template to be instantiated with a series of types listed on the containing directive (currently DECLARE_REDUCTION). Create a series of instantiations in the parser, allowing OpenMP special variables to be declared separately for each type. Patch is 73.19 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/165049.diff 24 Files Affected:
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index af8152deb8a52..002f67cbac6ca 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -596,7 +596,7 @@ class ParseTreeDumper {
NODE(parser, OmpInitClause)
NODE(OmpInitClause, Modifier)
NODE(parser, OmpInitializerClause)
- NODE(parser, OmpInitializerProc)
+ NODE(parser, OmpInitializerExpression)
NODE(parser, OmpInReductionClause)
NODE(OmpInReductionClause, Modifier)
NODE(parser, OmpInteropPreference)
@@ -674,6 +674,10 @@ class ParseTreeDumper {
NODE_ENUM(OmpSeverityClause, Severity)
NODE(parser, OmpStepComplexModifier)
NODE(parser, OmpStepSimpleModifier)
+ NODE(parser, OmpStylizedDeclaration)
+ NODE(parser, OmpStylizedExpression)
+ NODE(parser, OmpStylizedInstance)
+ NODE(OmpStylizedInstance, Instance)
NODE(parser, OmpTaskDependenceType)
NODE_ENUM(OmpTaskDependenceType, Value)
NODE(parser, OmpTaskReductionClause)
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index f761332c9cfd7..49db091af93a7 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -25,6 +25,13 @@
namespace Fortran::parser::omp {
+template <typename T> constexpr auto addr_if(std::optional<T> &x) {
+ return x ? &*x : nullptr;
+}
+template <typename T> constexpr auto addr_if(const std::optional<T> &x) {
+ return x ? &*x : nullptr;
+}
+
namespace detail {
using D = llvm::omp::Directive;
@@ -133,9 +140,24 @@ template <typename T> OmpDirectiveName GetOmpDirectiveName(const T &x) {
}
const OmpObjectList *GetOmpObjectList(const OmpClause &clause);
+
+template <typename T>
+const T *GetFirstArgument(const OmpDirectiveSpecification &spec) {
+ for (const OmpArgument &arg : spec.Arguments().v) {
+ if (auto *t{std::get_if<T>(&arg.u)}) {
+ return t;
+ }
+ }
+ return nullptr;
+}
+
const BlockConstruct *GetFortranBlockConstruct(
const ExecutionPartConstruct &epc);
+const OmpCombinerExpression *GetCombinerExpr(
+ const OmpReductionSpecifier &rspec);
+const OmpInitializerExpression *GetInitializerExpr(const OmpClause &init);
+
} // namespace Fortran::parser::omp
#endif // FORTRAN_PARSER_OPENMP_UTILS_H
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index be64ef3770c60..a3388a776cd32 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -24,7 +24,9 @@
#include "provenance.h"
#include "flang/Common/idioms.h"
#include "flang/Common/indirection.h"
+#include "flang/Common/reference.h"
#include "flang/Support/Fortran.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/Frontend/OpenACC/ACC.h.inc"
#include "llvm/Frontend/OpenMP/OMP.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
@@ -3504,6 +3506,8 @@ struct OmpDirectiveName {
// type-name list item
struct OmpTypeName {
+ CharBlock source;
+ mutable const semantics::DeclTypeSpec *declTypeSpec{nullptr};
UNION_CLASS_BOILERPLATE(OmpTypeName);
std::variant<TypeSpec, DeclarationTypeSpec> u;
};
@@ -3532,6 +3536,33 @@ struct OmpObjectList {
WRAPPER_CLASS_BOILERPLATE(OmpObjectList, std::list<OmpObject>);
};
+struct OmpStylizedDeclaration {
+ COPY_AND_ASSIGN_BOILERPLATE(OmpStylizedDeclaration);
+ // Since "Reference" isn't handled by parse-tree-visitor, add EmptyTrait,
+ // and visit the members by hand when needed.
+ using EmptyTrait = std::true_type;
+ common::Reference<const OmpTypeName> type;
+ EntityDecl var;
+};
+
+struct OmpStylizedInstance {
+ struct Instance {
+ UNION_CLASS_BOILERPLATE(Instance);
+ std::variant<AssignmentStmt, CallStmt, common::Indirection<Expr>> u;
+ };
+ TUPLE_CLASS_BOILERPLATE(OmpStylizedInstance);
+ std::tuple<std::list<OmpStylizedDeclaration>, Instance> t;
+};
+
+class ParseState;
+
+struct OmpStylizedExpression {
+ CharBlock source;
+ const ParseState *state{nullptr};
+ WRAPPER_CLASS_BOILERPLATE(
+ OmpStylizedExpression, std::list<OmpStylizedInstance>);
+};
+
// Ref: [4.5:201-207], [5.0:293-299], [5.1:325-331], [5.2:124]
//
// reduction-identifier ->
@@ -3549,9 +3580,22 @@ struct OmpReductionIdentifier {
// combiner-expression -> // since 4.5
// assignment-statement |
// function-reference
-struct OmpCombinerExpression {
- UNION_CLASS_BOILERPLATE(OmpCombinerExpression);
- std::variant<AssignmentStmt, FunctionReference> u;
+struct OmpCombinerExpression : public OmpStylizedExpression {
+ INHERITED_WRAPPER_CLASS_BOILERPLATE(
+ OmpCombinerExpression, OmpStylizedExpression);
+ static llvm::ArrayRef<CharBlock> Variables();
+};
+
+// Ref: [4.5:222:7-8], [5.0:305:28-29], [5.1:337:20-21], [5.2:127:6-8],
+// [6.0:242:3-5]
+//
+// initializer-expression -> // since 4.5
+// OMP_PRIV = expression |
+// subroutine-name(argument-list)
+struct OmpInitializerExpression : public OmpStylizedExpression {
+ INHERITED_WRAPPER_CLASS_BOILERPLATE(
+ OmpInitializerExpression, OmpStylizedExpression);
+ static llvm::ArrayRef<CharBlock> Variables();
};
inline namespace arguments {
@@ -4552,16 +4596,9 @@ struct OmpInReductionClause {
std::tuple<MODIFIERS(), OmpObjectList> t;
};
-// declare-reduction -> DECLARE REDUCTION (reduction-identifier : type-list
-// : combiner) [initializer-clause]
-struct OmpInitializerProc {
- TUPLE_CLASS_BOILERPLATE(OmpInitializerProc);
- std::tuple<ProcedureDesignator, std::list<ActualArgSpec>> t;
-};
// Initialization for declare reduction construct
struct OmpInitializerClause {
- UNION_CLASS_BOILERPLATE(OmpInitializerClause);
- std::variant<OmpInitializerProc, AssignmentStmt> u;
+ WRAPPER_CLASS_BOILERPLATE(OmpInitializerClause, OmpInitializerExpression);
};
// Ref: [4.5:199-201], [5.0:288-290], [5.1:321-322], [5.2:115-117]
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 04a063957082a..cb27d544ed9f5 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -830,6 +830,8 @@ class Symbol {
OmpUseDevicePtr, OmpUseDeviceAddr, OmpIsDevicePtr, OmpHasDeviceAddr,
// OpenMP data-copying attribute
OmpCopyIn, OmpCopyPrivate,
+ // OpenMP special variables
+ OmpInVar, OmpOrigVar, OmpOutVar, OmpPrivVar,
// OpenMP miscellaneous flags
OmpCommonBlock, OmpReduction, OmpInReduction, OmpAligned, OmpNontemporal,
OmpAllocate, OmpDeclarativeAllocateDirective,
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index d1e081cfd1b41..1f473660003a9 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -275,6 +275,13 @@ struct SpecificModifierParser {
// --- Iterator helpers -----------------------------------------------
+static EntityDecl MakeEntityDecl(ObjectName &&name) {
+ return EntityDecl(
+ /*ObjectName=*/std::move(name), std::optional<ArraySpec>{},
+ std::optional<CoarraySpec>{}, std::optional<CharLength>{},
+ std::optional<Initialization>{});
+}
+
// [5.0:47:17-18] In an iterator-specifier, if the iterator-type is not
// specified then the type of that iterator is default integer.
// [5.0:49:14] The iterator-type must be an integer type.
@@ -282,11 +289,7 @@ static std::list<EntityDecl> makeEntityList(std::list<ObjectName> &&names) {
std::list<EntityDecl> entities;
for (auto iter = names.begin(), end = names.end(); iter != end; ++iter) {
- EntityDecl entityDecl(
- /*ObjectName=*/std::move(*iter), std::optional<ArraySpec>{},
- std::optional<CoarraySpec>{}, std::optional<CharLength>{},
- std::optional<Initialization>{});
- entities.push_back(std::move(entityDecl));
+ entities.push_back(MakeEntityDecl(std::move(*iter)));
}
return entities;
}
@@ -306,6 +309,207 @@ static TypeDeclarationStmt makeIterSpecDecl(std::list<ObjectName> &&names) {
makeEntityList(std::move(names)));
}
+// --- Stylized expression handling -----------------------------------
+
+// OpenMP has a concept of am "OpenMP stylized expression". Syntactially
+// it looks like a typical Fortran expression (or statement), except:
+// - the only variables allowed in it are OpenMP special variables, the
+// exact set of these variables depends on the specific case of the
+// stylized expression
+// - the special OpenMP variables present may assume one or more types,
+// and the expression should be semantically valid for each type.
+//
+// The stylized expression can be thought of as a template, which will be
+// instantiated for each type provided somewhere in the context in which
+// the stylized expression appears.
+//
+// AST nodes:
+// - OmpStylizedExpression: contains the source string for the expression,
+// plus the list of instances (OmpStylizedInstance).
+// - OmpStylizedInstance: corresponds to the instantiation of the stylized
+// expression for a specific type. The way that the type is specified is
+// by creating declarations (OmpStylizedDeclaration) for the special
+// variables. Together with the AST tree corresponding to the stylized
+// expression the instantiation has enough information for semantic
+// analysis. Each instance has its own scope, and the special variables
+// have their own Symbol's (local to the scope).
+// - OmpStylizedDeclaration: encapsulates the information that the visitors
+// in resolve-names can use to "emulate" a declaration for a special
+// variable and allow name resolution in the instantiation AST to work.
+//
+// Implementation specifics:
+// The semantic analysis stores "evaluate::Expr" in each AST node rooted
+// in parser::Expr (in the typedExpr member). The evaluate::Expr is specific
+// to a given type, and so to allow different types for a given expression,
+// for each type a separate copy of the parsee::Expr subtree is created.
+// Normally, AST nodes are non-copyable (copy-ctor is deleted), so to create
+// several copies of a subtree, the same source string is parsed several
+// times. The ParseState member in OmpStylizedExpression is the parser state
+// immediately before the stylized expression.
+//
+// Initially, when OmpStylizedExpression is first created, the expression is
+// parsed as if it was an actual code, but this parsing is only done to
+// establish where the stylized expression ends (in the source). The source
+// and the initial parser state are store in the object, and the instance
+// list is empty.
+// Once the parsing of the containing OmpDirectiveSpecification completes,
+// a post-processing "parser" (OmpStylizedInstanceCreator) executes. This
+// post-processor examines the directive specification to see if it expects
+// any stylized expressions to be contained in it, and then instantiates
+// them for each such directive.
+
+template <typename A> struct NeverParser {
+ using resultType = A;
+ std::optional<resultType> Parse(ParseState &state) const {
+ // Always fail, but without any messages.
+ return std::nullopt;
+ }
+};
+
+template <typename A> constexpr auto never() { return NeverParser<A>{}; }
+
+template <typename A, typename B = void> struct NullParser;
+template <typename B> struct NullParser<std::optional<B>> {
+ using resultType = std::optional<B>;
+ std::optional<resultType> Parse(ParseState &) const {
+ return resultType{std::nullopt};
+ }
+};
+
+template <typename A> constexpr auto null() { return NullParser<A>{}; }
+
+// OmpStylizedDeclaration and OmpStylizedInstance are helper classes, and
+// don't correspond to anything in the source. Their parsers should still
+// exist, but they should never be executed.
+TYPE_PARSER(construct<OmpStylizedDeclaration>(never<OmpStylizedDeclaration>()))
+
+TYPE_PARSER( //
+ construct<OmpStylizedInstance::Instance>(Parser<AssignmentStmt>{}) ||
+ construct<OmpStylizedInstance::Instance>(
+ sourced(construct<CallStmt>(Parser<ProcedureDesignator>{},
+ null<std::optional<CallStmt::Chevrons>>(),
+ parenthesized(optionalList(actualArgSpec))))) ||
+ construct<OmpStylizedInstance::Instance>(indirect(expr)))
+
+TYPE_PARSER(construct<OmpStylizedInstance>(never<OmpStylizedInstance>()))
+
+struct OmpStylizedExpressionParser {
+ using resultType = OmpStylizedExpression;
+
+ std::optional<resultType> Parse(ParseState &state) const {
+ auto *saved{new ParseState(state)};
+ auto getSource{verbatim(Parser<OmpStylizedInstance::Instance>{} >> ok)};
+ if (auto &&ok{getSource.Parse(state)}) {
+ OmpStylizedExpression result{std::list<OmpStylizedInstance>{}};
+ result.source = ok->source;
+ result.state = saved;
+ // result.v remains empty
+ return std::move(result);
+ }
+ delete saved;
+ return std::nullopt;
+ }
+};
+
+static void Instantiate(OmpStylizedExpression &ose,
+ llvm::ArrayRef<const OmpTypeName *> types, llvm::ArrayRef<CharBlock> vars) {
+ // 1. For each var in the vars list, declare it with the corresponding
+ // type from types.
+ // 2. Run the parser to get the AST for the stylized expression.
+ // 3. Create OmpStylizedInstance and append it to the list in ose.
+ assert(types.size() == vars.size() && "List size mismatch");
+ ParseState state{DEREF(ose.state)};
+
+ std::list<OmpStylizedDeclaration> decls;
+ for (auto [type, var] : llvm::zip_equal(types, vars)) {
+ decls.emplace_back(OmpStylizedDeclaration{
+ common::Reference(*type), MakeEntityDecl(Name{var})});
+ }
+
+ if (auto &&instance{Parser<OmpStylizedInstance::Instance>{}.Parse(state)}) {
+ ose.v.emplace_back(
+ OmpStylizedInstance{std::move(decls), std::move(*instance)});
+ }
+}
+
+static void InstantiateForTypes(OmpStylizedExpression &ose,
+ const OmpTypeNameList &typeNames, llvm::ArrayRef<CharBlock> vars) {
+ // For each type in the type list, declare all variables in vars with
+ // that type, and complete the instantiation.
+ for (const OmpTypeName &t : typeNames.v) {
+ std::vector<const OmpTypeName *> types(vars.size(), &t);
+ Instantiate(ose, types, vars);
+ }
+}
+
+static void InstantiateDeclareReduction(OmpDirectiveSpecification &spec) {
+ // There can be arguments/clauses that don't make sense, that analysis
+ // is left until semantic checks. Tolerate any unexpected stuff.
+ auto *rspec{GetFirstArgument<OmpReductionSpecifier>(spec)};
+ if (!rspec) {
+ return;
+ }
+
+ const OmpTypeNameList *typeNames{nullptr};
+
+ if (auto *cexpr{GetCombinerExpr(*rspec)}) {
+ typeNames = &std::get<OmpTypeNameList>(rspec->t);
+
+ InstantiateForTypes(const_cast<OmpCombinerExpression &>(*cexpr), *typeNames,
+ OmpCombinerExpression::Variables());
+ delete cexpr->state;
+ } 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 (auto *iexpr{GetInitializerExpr(clause)}) {
+ InstantiateForTypes(const_cast<OmpInitializerExpression &>(*iexpr),
+ *typeNames, OmpInitializerExpression::Variables());
+ delete iexpr->state;
+ }
+ }
+ }
+}
+
+static void InstantiateStylizedDirective(OmpDirectiveSpecification &spec) {
+ const OmpDirectiveName &dirName{spec.DirName()};
+ if (dirName.v == llvm::omp::Directive::OMPD_declare_reduction) {
+ InstantiateDeclareReduction(spec);
+ }
+}
+
+template <typename P,
+ typename = std::enable_if_t<
+ std::is_same_v<typename P::resultType, OmpDirectiveSpecification>>>
+struct OmpStylizedInstanceCreator {
+ using resultType = OmpDirectiveSpecification;
+ constexpr OmpStylizedInstanceCreator(P p) : parser_(p) {}
+
+ std::optional<resultType> Parse(ParseState &state) const {
+ if (auto &&spec{parser_.Parse(state)}) {
+ InstantiateStylizedDirective(*spec);
+ return std::move(spec);
+ }
+ return std::nullopt;
+ }
+
+private:
+ const P parser_;
+};
+
+template <typename P>
+OmpStylizedInstanceCreator(P) -> OmpStylizedInstanceCreator<P>;
+
+// --- Parsers for types ----------------------------------------------
+
+TYPE_PARSER( //
+ sourced(construct<OmpTypeName>(Parser<DeclarationTypeSpec>{})) ||
+ sourced(construct<OmpTypeName>(Parser<TypeSpec>{})))
+
// --- Parsers for arguments ------------------------------------------
// At the moment these are only directive arguments. This is needed for
@@ -366,10 +570,6 @@ struct OmpArgumentListParser {
}
};
-TYPE_PARSER( //
- construct<OmpTypeName>(Parser<DeclarationTypeSpec>{}) ||
- construct<OmpTypeName>(Parser<TypeSpec>{}))
-
// 2.15.3.6 REDUCTION (reduction-identifier: variable-name-list)
TYPE_PARSER(construct<OmpReductionIdentifier>(Parser<DefinedOperator>{}) ||
construct<OmpReductionIdentifier>(Parser<ProcedureDesignator>{}))
@@ -1065,7 +1265,8 @@ TYPE_PARSER(construct<OmpOtherwiseClause>(
TYPE_PARSER(construct<OmpWhenClause>(
maybe(nonemptyList(Parser<OmpWhenClause::Modifier>{}) / ":"),
- maybe(indirect(Parser<OmpDirectiveSpecification>{}))))
+ maybe(indirect(
+ OmpStylizedInstanceCreator(Parser<OmpDirectiveSpecification>{})))))
// OMP 5.2 12.6.1 grainsize([ prescriptiveness :] scalar-integer-expression)
TYPE_PARSER(construct<OmpGrainsizeClause>(
@@ -1777,12 +1978,7 @@ TYPE_PARSER(
Parser<OpenMPInteropConstruct>{})) /
endOfLine)
-TYPE_PARSER(construct<OmpInitializerProc>(Parser<ProcedureDesignator>{},
- parenthesized(many(maybe(","_tok) >> Parser<ActualArgSpec>{}))))
-
-TYPE_PARSER(construct<OmpInitializerClause>(
- construct<OmpInitializerClause>(assignmentStmt) ||
- construct<OmpInitializerClause>(Parser<OmpInitializerProc>{})))
+TYPE_PARSER(construct<OmpInitializerClause>(Parser<OmpInitializerExpression>{}))
// OpenMP 5.2: 7.5.4 Declare Variant directive
TYPE_PARSER(sourced(construct<OmpDeclareVariantDirective>(
@@ -1794,7 +1990,7 @@ TYPE_PARSER(sourced(construct<OmpDeclareVariantDirective>(
TYPE_PARSER(sourced(construct<OpenMPDeclareReductionConstruct>(
predicated(Parser<OmpDirectiveName>{},
IsDirective(llvm::omp::Directive::OMPD_declare_reduction)) >=
- Parser<OmpDirectiveSpecification>{})))
+ OmpStylizedInstanceCreator(Parser<OmpDirectiveSpecification>{}))))
// 2.10.6 Declare Target Construct
TYPE_PARSER(sourced(construct<OpenMPDeclareTargetConstruct>(
@@ -1832,8 +2028,8 @@ TYPE_PARSER(sourced(construct<OpenMPDeclareMapperConstruct>(
IsDirective(llvm::omp::Directive::OMPD_declare_mapper)) >=
Parser<OmpDirectiveSpecification>{})))
-TYPE_PARSER(construct<OmpCombinerExpression>(Parser<AssignmentStmt>{}) ||
- construct<OmpCombinerExpression>(Parser<FunctionReference>{}))
+TYPE_PARSER(construct<OmpCombinerExpression>(OmpStylizedExpressionParser{}))
+TYPE_PARSER(construct<OmpInitializerExpression>(OmpStylizedExpressionParser{}))
TYPE_PARSER(sourced(construct<OpenMPCriticalConstruct>(
OmpBlockConstructParser{llvm::omp::Directive::OMPD_critical})))
diff --git a/flang/lib/Parser/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp
index 937a17f29f221..95ad3f60770f5 100644
--- a/flang/lib/Parser/openmp-utils.cpp
+++ b/flang/lib/Parser/openmp-utils.cpp
@@ -74,4 +74,16 @@ const BlockConstruct *GetFortranBlockConstruct(
return nullptr;
}
+const OmpCombinerExpression *GetCombinerExpr(
+ const OmpReductionSpecifier &rspec) {
+ return addr_if(std::get<std::optional<OmpCombinerExpression>>(rspec.t));
+}
+
+const OmpInitializerExpression *GetInitializerExpr(const OmpClause &init) {
+ if (auto *wrapped{std::get_if<OmpClause::Initializer>(&init.u)}) {
+ return &wrapped->v.v;
+ }
+ return nullptr;
+}
+
} // namespace Fortran::parser::omp
diff --git a/flang/lib/Parser/parse-tree.cpp b/flang/lib/Parser/parse-tree.cpp
index 8cbaa399c4763..ad0016e1404f9 100644
--- a/flang/lib/Parser/parse-tree.cpp
+++ b/flang/lib/Parser/parse-tree.cpp
@@ -11,6 +11,7 @@
#include "flang/Common/indirection.h"
#include "flang/Parser/tools.h"
#include "flang/Parser/user-state.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/Frontend/OpenMP/OMP.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -430,4 +431,30 @@ const OmpClauseList &OmpDirectiveSpecification::Clauses() const {
...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I prefer this approach. Just some minor comments.
Co-authored-by: Tom Eccles <tom.eccles@arm.com>
Co-authored-by: Tom Eccles <tom.eccles@arm.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great to me. Thanks for the updates.
Consider OpenMP stylized expression to be a template to be instantiated with a series of types listed on the containing directive (currently DECLARE_REDUCTION). Create a series of instantiations in the parser, allowing OpenMP special variables to be declared separately for each type. --------- Co-authored-by: Tom Eccles <tom.eccles@arm.com>
Consider OpenMP stylized expression to be a template to be instantiated with a series of types listed on the containing directive (currently DECLARE_REDUCTION). Create a series of instantiations in the parser, allowing OpenMP special variables to be declared separately for each type.