[flang-commits] [flang] [llvm] [flang][OpenMP] Decompose compound constructs, do recursive lowering (PR #90098)
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Tue May 7 06:12:42 PDT 2024
================
@@ -0,0 +1,1161 @@
+//===- ConstructDecompositionT.h -- Decomposing compound constructs -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// Given a compound construct with a set of clauses, generate the list of
+// constituent leaf constructs, each with a list of clauses that apply to it.
+//
+// Note: Clauses that are not originally present, but that are implied by the
+// OpenMP spec are materialized, and are present in the output.
+//
+// Note: Composite constructs will also be broken up into leaf constructs.
+// If composite constructs require processing as a whole, the lists of clauses
+// for each leaf constituent should be merged.
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
+#define LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Frontend/OpenMP/ClauseT.h"
+#include "llvm/Frontend/OpenMP/OMP.h"
+
+#include <iterator>
+#include <list>
+#include <optional>
+#include <tuple>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <variant>
+
+static inline llvm::ArrayRef<llvm::omp::Directive> getWorksharing() {
+ static llvm::omp::Directive worksharing[] = {
+ llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_for,
+ llvm::omp::Directive::OMPD_scope, llvm::omp::Directive::OMPD_sections,
+ llvm::omp::Directive::OMPD_single, llvm::omp::Directive::OMPD_workshare,
+ };
+ return worksharing;
+}
+
+static inline llvm::ArrayRef<llvm::omp::Directive> getWorksharingLoop() {
+ static llvm::omp::Directive worksharingLoop[] = {
+ llvm::omp::Directive::OMPD_do,
+ llvm::omp::Directive::OMPD_for,
+ };
+ return worksharingLoop;
+}
+
+namespace detail {
+template <typename Container, typename Predicate>
+typename std::remove_reference_t<Container>::iterator
+find_unique(Container &&container, Predicate &&pred) {
+ auto first = std::find_if(container.begin(), container.end(), pred);
+ if (first == container.end())
+ return first;
+ auto second = std::find_if(std::next(first), container.end(), pred);
+ if (second == container.end())
+ return first;
+ return container.end();
+}
+
+} // namespace detail
+
+namespace tomp {
+
+// ClauseType - Either instance of ClauseT, or a type derived from ClauseT.
+//
+// This is the clause representation in the code using this infrastructure.
+//
+// HelperType - A class that implements two member functions:
+//
+// // Return the base object of the given object, if any.
+// std::optional<Object> getBaseObject(const Object &object) const
+// // Return the iteration variable of the outermost loop associated
+// // with the construct being worked on, if any.
+// std::optional<Object> getLoopIterVar() const
+template <typename ClauseType, typename HelperType>
+struct ConstructDecompositionT {
+ using ClauseTy = ClauseType;
+
+ using TypeTy = typename ClauseTy::TypeTy;
+ using IdTy = typename ClauseTy::IdTy;
+ using ExprTy = typename ClauseTy::ExprTy;
+ using HelperTy = HelperType;
+ using ObjectTy = tomp::ObjectT<IdTy, ExprTy>;
+
+ using ClauseSet = std::unordered_set<const ClauseTy *>;
+
+ ConstructDecompositionT(uint32_t ver, HelperType &hlp,
+ llvm::omp::Directive dir,
+ llvm::ArrayRef<ClauseTy> clauses)
+ : version(ver), construct(dir), helper(hlp) {
+ for (const ClauseTy &clause : clauses)
+ nodes.push_back(&clause);
+
+ bool success = split();
+ if (!success)
+ return;
+
+ // Copy the individual leaf directives with their clauses to the
+ // output list. Copy by value, since we don't own the storage
+ // with the input clauses, and the internal representation uses
+ // clause addresses.
+ for (auto &leaf : leafs) {
+ output.push_back({leaf.id});
+ auto &out = output.back();
+ for (const ClauseTy *c : leaf.clauses)
+ out.clauses.push_back(*c);
+ }
+ }
+
+ tomp::ListT<DirectiveWithClauses<ClauseType>> output;
+
+private:
+ bool split();
+
+ struct LeafReprInternal {
+ llvm::omp::Directive id = llvm::omp::Directive::OMPD_unknown;
+ tomp::type::ListT<const ClauseTy *> clauses;
+ };
+
+ LeafReprInternal *findDirective(llvm::omp::Directive dirId) {
+ auto found = llvm::find_if(
+ leafs, [&](const LeafReprInternal &leaf) { return leaf.id == dirId; });
+ return found != leafs.end() ? &*found : nullptr;
+ }
+
+ ClauseSet *findClausesWith(const ObjectTy &object) {
+ if (auto found = syms.find(object.id()); found != syms.end())
+ return &found->second;
+ return nullptr;
+ }
+
+ template <typename S>
+ ClauseTy *makeClause(llvm::omp::Clause clauseId, S &&specific) {
+ implicit.push_back(ClauseTy{clauseId, std::move(specific)});
+ return &implicit.back();
+ }
+
+ void addClauseSymsToMap(const ObjectTy &object, const ClauseTy *);
+ void addClauseSymsToMap(const tomp::ObjectListT<IdTy, ExprTy> &objects,
+ const ClauseTy *);
+ void addClauseSymsToMap(const TypeTy &item, const ClauseTy *);
+ void addClauseSymsToMap(const ExprTy &item, const ClauseTy *);
+ void addClauseSymsToMap(const tomp::clause::MapT<TypeTy, IdTy, ExprTy> &item,
+ const ClauseTy *);
+
+ template <typename U>
+ void addClauseSymsToMap(const std::optional<U> &item, const ClauseTy *);
+ template <typename U>
+ void addClauseSymsToMap(const tomp::ListT<U> &item, const ClauseTy *);
+ template <typename... U, size_t... Is>
+ void addClauseSymsToMap(const std::tuple<U...> &item, const ClauseTy *,
+ std::index_sequence<Is...> = {});
+ template <typename U>
+ std::enable_if_t<std::is_enum_v<llvm::remove_cvref_t<U>>, void>
+ addClauseSymsToMap(U &&item, const ClauseTy *);
+
+ template <typename U>
+ std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value, void>
+ addClauseSymsToMap(U &&item, const ClauseTy *);
+
+ template <typename U>
+ std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value, void>
+ addClauseSymsToMap(U &&item, const ClauseTy *);
+
+ template <typename U>
+ std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value, void>
+ addClauseSymsToMap(U &&item, const ClauseTy *);
+
+ template <typename U>
+ std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value, void>
+ addClauseSymsToMap(U &&item, const ClauseTy *);
+
+ template <typename U>
+ std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value, void>
+ addClauseSymsToMap(U &&item, const ClauseTy *);
+
+ // Apply a clause to the only directive that allows it. If there are no
+ // directives that allow it, or if there is more that one, do not apply
+ // anything and return false, otherwise return true.
+ bool applyToUnique(const ClauseTy *node);
+
+ // Apply a clause to the first directive in given range that allows it.
+ // If such a directive does not exist, return false, otherwise return true.
+ template <typename Iterator>
+ bool applyToFirst(const ClauseTy *node, llvm::iterator_range<Iterator> range);
+
+ // Apply a clause to the innermost directive that allows it. If such a
+ // directive does not exist, return false, otherwise return true.
+ bool applyToInnermost(const ClauseTy *node);
+
+ // Apply a clause to the outermost directive that allows it. If such a
+ // directive does not exist, return false, otherwise return true.
+ bool applyToOutermost(const ClauseTy *node);
+
+ template <typename Predicate>
+ bool applyIf(const ClauseTy *node, Predicate shouldApply);
+
+ bool applyToAll(const ClauseTy *node);
+
+ template <typename Clause>
+ bool applyClause(Clause &&clause, const ClauseTy *node);
+
+ bool applyClause(const tomp::clause::CollapseT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool applyClause(const tomp::clause::PrivateT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool
+ applyClause(const tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool
+ applyClause(const tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool applyClause(const tomp::clause::SharedT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool applyClause(const tomp::clause::DefaultT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool
+ applyClause(const tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool applyClause(const tomp::clause::OrderT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool applyClause(const tomp::clause::AllocateT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool applyClause(const tomp::clause::ReductionT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool applyClause(const tomp::clause::IfT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool applyClause(const tomp::clause::LinearT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+ bool applyClause(const tomp::clause::NowaitT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *);
+
+ uint32_t version;
+ llvm::omp::Directive construct;
+ HelperType &helper;
+ ListT<LeafReprInternal> leafs;
+ tomp::ListT<const ClauseTy *> nodes;
+ std::list<ClauseTy> implicit; // Container for materialized implicit clauses.
+ // Inserting must preserve element addresses.
+ std::unordered_map<IdTy, ClauseSet> syms;
+ std::unordered_set<IdTy> mapBases;
+};
+
+// Deduction guide
+template <typename ClauseType, typename HelperType>
+ConstructDecompositionT(uint32_t, HelperType &, llvm::omp::Directive,
+ llvm::ArrayRef<ClauseType>)
+ -> ConstructDecompositionT<ClauseType, HelperType>;
+
+template <typename C, typename H>
+void ConstructDecompositionT<C, H>::addClauseSymsToMap(const ObjectTy &object,
+ const ClauseTy *node) {
+ syms[object.id()].insert(node);
+}
+
+template <typename C, typename H>
+void ConstructDecompositionT<C, H>::addClauseSymsToMap(
+ const tomp::ObjectListT<IdTy, ExprTy> &objects, const ClauseTy *node) {
+ for (auto &object : objects)
+ syms[object.id()].insert(node);
+}
+
+template <typename C, typename H>
+void ConstructDecompositionT<C, H>::addClauseSymsToMap(const TypeTy &item,
+ const ClauseTy *node) {
+ // Nothing to do for types.
+}
+
+template <typename C, typename H>
+void ConstructDecompositionT<C, H>::addClauseSymsToMap(const ExprTy &item,
+ const ClauseTy *node) {
+ // Nothing to do for expressions.
+}
+
+template <typename C, typename H>
+void ConstructDecompositionT<C, H>::addClauseSymsToMap(
+ const tomp::clause::MapT<TypeTy, IdTy, ExprTy> &item,
+ const ClauseTy *node) {
+ auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(item.t);
+ addClauseSymsToMap(objects, node);
+ for (auto &object : objects) {
+ if (auto base = helper.getBaseObject(object))
+ mapBases.insert(base->id());
+ }
+}
+
+template <typename C, typename H>
+template <typename U>
+void ConstructDecompositionT<C, H>::addClauseSymsToMap(
+ const std::optional<U> &item, const ClauseTy *node) {
+ if (item)
+ addClauseSymsToMap(*item, node);
+}
+
+template <typename C, typename H>
+template <typename U>
+void ConstructDecompositionT<C, H>::addClauseSymsToMap(
+ const tomp::ListT<U> &item, const ClauseTy *node) {
+ for (auto &s : item)
+ addClauseSymsToMap(s, node);
+}
+
+template <typename C, typename H>
+template <typename... U, size_t... Is>
+void ConstructDecompositionT<C, H>::addClauseSymsToMap(
+ const std::tuple<U...> &item, const ClauseTy *node,
+ std::index_sequence<Is...>) {
+ (void)node; // Silence strange warning from GCC.
+ (addClauseSymsToMap(std::get<Is>(item), node), ...);
+}
+
+template <typename C, typename H>
+template <typename U>
+std::enable_if_t<std::is_enum_v<llvm::remove_cvref_t<U>>, void>
+ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
+ const ClauseTy *node) {
+ // Nothing to do for enums.
+}
+
+template <typename C, typename H>
+template <typename U>
+std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value, void>
+ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
+ const ClauseTy *node) {
+ // Nothing to do for an empty class.
+}
+
+template <typename C, typename H>
+template <typename U>
+std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value, void>
+ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
+ const ClauseTy *node) {
+ // Nothing to do for an incomplete class (they're empty).
+}
+
+template <typename C, typename H>
+template <typename U>
+std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value, void>
+ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
+ const ClauseTy *node) {
+ addClauseSymsToMap(item.v, node);
+}
+
+template <typename C, typename H>
+template <typename U>
+std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value, void>
+ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
+ const ClauseTy *node) {
+ constexpr size_t tuple_size =
+ std::tuple_size_v<llvm::remove_cvref_t<decltype(item.t)>>;
+ addClauseSymsToMap(item.t, node, std::make_index_sequence<tuple_size>{});
+}
+
+template <typename C, typename H>
+template <typename U>
+std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value, void>
+ConstructDecompositionT<C, H>::addClauseSymsToMap(U &&item,
+ const ClauseTy *node) {
+ std::visit([&](auto &&s) { addClauseSymsToMap(s, node); }, item.u);
+}
+
+// Apply a clause to the only directive that allows it. If there are no
+// directives that allow it, or if there is more that one, do not apply
+// anything and return false, otherwise return true.
+template <typename C, typename H>
+bool ConstructDecompositionT<C, H>::applyToUnique(const ClauseTy *node) {
+ auto unique = detail::find_unique(leafs, [=](const auto &dirInfo) {
+ return llvm::omp::isAllowedClauseForDirective(dirInfo.id, node->id,
+ version);
+ });
+
+ if (unique != leafs.end()) {
+ unique->clauses.push_back(node);
+ return true;
+ }
+ return false;
+}
+
+// Apply a clause to the first directive in given range that allows it.
+// If such a directive does not exist, return false, otherwise return true.
+template <typename C, typename H>
+template <typename Iterator>
+bool ConstructDecompositionT<C, H>::applyToFirst(
+ const ClauseTy *node, llvm::iterator_range<Iterator> range) {
+ if (range.empty())
+ return false;
+
+ for (auto &leaf : range) {
+ if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
+ continue;
+ leaf.clauses.push_back(node);
+ return true;
+ }
+ return false;
+}
+
+// Apply a clause to the innermost directive that allows it. If such a
+// directive does not exist, return false, otherwise return true.
+template <typename C, typename H>
+bool ConstructDecompositionT<C, H>::applyToInnermost(const ClauseTy *node) {
+ return applyToFirst(node, llvm::reverse(leafs));
+}
+
+// Apply a clause to the outermost directive that allows it. If such a
+// directive does not exist, return false, otherwise return true.
+template <typename C, typename H>
+bool ConstructDecompositionT<C, H>::applyToOutermost(const ClauseTy *node) {
+ return applyToFirst(node, llvm::iterator_range(leafs));
+}
+
+template <typename C, typename H>
+template <typename Predicate>
+bool ConstructDecompositionT<C, H>::applyIf(const ClauseTy *node,
+ Predicate shouldApply) {
+ bool applied = false;
+ for (auto &leaf : leafs) {
+ if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
+ continue;
+ if (!shouldApply(leaf))
+ continue;
+ leaf.clauses.push_back(node);
+ applied = true;
+ }
+
+ return applied;
+}
+
+template <typename C, typename H>
+bool ConstructDecompositionT<C, H>::applyToAll(const ClauseTy *node) {
+ return applyIf(node, [](auto) { return true; });
+}
+
+template <typename C, typename H>
+template <typename Clause>
+bool ConstructDecompositionT<C, H>::applyClause(Clause &&clause,
+ const ClauseTy *node) {
+ // The default behavior is to find the unique directive to which the
+ // given clause may be applied. If there are no such directives, or
+ // if there are multiple ones, flag an error.
+ // From "OpenMP Application Programming Interface", Version 5.2:
+ // S Some clauses are permitted only on a single leaf construct of the
+ // S combined or composite construct, in which case the effect is as if
+ // S the clause is applied to that specific construct. (p339, 31-33)
+ if (applyToUnique(node))
+ return true;
+
+ return false;
+}
+
+// COLLAPSE
+// [5.2:93:20-21]
+// Directives: distribute, do, for, loop, simd, taskloop
+//
+// [5.2:339:35]
+// (35) The collapse clause is applied once to the combined or composite
+// construct.
+template <typename C, typename H>
+bool ConstructDecompositionT<C, H>::applyClause(
+ const tomp::clause::CollapseT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *node) {
+ // Apply "collapse" to the innermost directive. If it's not one that
+ // allows it flag an error.
+ if (!leafs.empty()) {
+ auto &last = leafs.back();
+
+ if (llvm::omp::isAllowedClauseForDirective(last.id, node->id, version)) {
+ last.clauses.push_back(node);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// PRIVATE
+// [5.2:111:5-7]
+// Directives: distribute, do, for, loop, parallel, scope, sections, simd,
+// single, target, task, taskloop, teams
+//
+// [5.2:340:1-2]
+// (1) The effect of the 1 private clause is as if it is applied only to the
+// innermost leaf construct that permits it.
+template <typename C, typename H>
+bool ConstructDecompositionT<C, H>::applyClause(
+ const tomp::clause::PrivateT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *node) {
+ return applyToInnermost(node);
+}
+
+// FIRSTPRIVATE
+// [5.2:112:5-7]
+// Directives: distribute, do, for, parallel, scope, sections, single, target,
+// task, taskloop, teams
+//
+// [5.2:340:3-20]
+// (3) The effect of the firstprivate clause is as if it is applied to one or
+// more leaf constructs as follows:
+// (5) To the distribute construct if it is among the constituent constructs;
+// (6) To the teams construct if it is among the constituent constructs and the
+// distribute construct is not;
+// (8) To a worksharing construct that accepts the clause if one is among the
+// constituent constructs;
+// (9) To the taskloop construct if it is among the constituent constructs;
+// (10) To the parallel construct if it is among the constituent constructs and
+// neither a taskloop construct nor a worksharing construct that accepts
+// the clause is among them;
+// (12) To the target construct if it is among the constituent constructs and
+// the same list item neither appears in a lastprivate clause nor is the
+// base variable or base pointer of a list item that appears in a map
+// clause.
+//
+// (15) If the parallel construct is among the constituent constructs and the
+// effect is not as if the firstprivate clause is applied to it by the above
+// rules, then the effect is as if the shared clause with the same list item is
+// applied to the parallel construct.
+// (17) If the teams construct is among the constituent constructs and the
+// effect is not as if the firstprivate clause is applied to it by the above
+// rules, then the effect is as if the shared clause with the same list item is
+// applied to the teams construct.
+template <typename C, typename H>
+bool ConstructDecompositionT<C, H>::applyClause(
+ const tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy> &clause,
+ const ClauseTy *node) {
+ bool applied = false;
+
+ // [5.2:340:3-6]
+ auto hasDistribute = findDirective(llvm::omp::OMPD_distribute);
----------------
kparzysz wrote:
Done, replaced with `dirXyz`.
https://github.com/llvm/llvm-project/pull/90098
More information about the flang-commits
mailing list