clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
1182 lines
43 KiB
C++
1182 lines
43 KiB
C++
//===- 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 &helper,
|
|
llvm::omp::Directive dir,
|
|
llvm::ArrayRef<ClauseTy> clauses)
|
|
: version(ver), construct(dir), helper(helper) {
|
|
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(typename ClauseTy::BaseT{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 *);
|
|
bool
|
|
applyClause(const tomp::clause::OmpxAttributeT<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 &leaf) {
|
|
return llvm::omp::isAllowedClauseForDirective(leaf.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 Specific>
|
|
bool ConstructDecompositionT<C, H>::applyClause(Specific &&specific,
|
|
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 dirDistribute = findDirective(llvm::omp::OMPD_distribute);
|
|
auto dirTeams = findDirective(llvm::omp::OMPD_teams);
|
|
if (dirDistribute != nullptr) {
|
|
dirDistribute->clauses.push_back(node);
|
|
applied = true;
|
|
// [5.2:340:17]
|
|
if (dirTeams != nullptr) {
|
|
auto *shared = makeClause(
|
|
llvm::omp::Clause::OMPC_shared,
|
|
tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/clause.v});
|
|
dirTeams->clauses.push_back(shared);
|
|
}
|
|
} else if (dirTeams != nullptr) {
|
|
dirTeams->clauses.push_back(node);
|
|
applied = true;
|
|
}
|
|
|
|
// [5.2:340:8]
|
|
auto findWorksharing = [&]() {
|
|
auto worksharing = getWorksharing();
|
|
for (auto &leaf : leafs) {
|
|
auto found = llvm::find(worksharing, leaf.id);
|
|
if (found != std::end(worksharing))
|
|
return &leaf;
|
|
}
|
|
return static_cast<typename decltype(leafs)::value_type *>(nullptr);
|
|
};
|
|
|
|
auto dirWorksharing = findWorksharing();
|
|
if (dirWorksharing != nullptr) {
|
|
dirWorksharing->clauses.push_back(node);
|
|
applied = true;
|
|
}
|
|
|
|
// [5.2:340:9]
|
|
auto dirTaskloop = findDirective(llvm::omp::OMPD_taskloop);
|
|
if (dirTaskloop != nullptr) {
|
|
dirTaskloop->clauses.push_back(node);
|
|
applied = true;
|
|
}
|
|
|
|
// [5.2:340:10]
|
|
auto dirParallel = findDirective(llvm::omp::OMPD_parallel);
|
|
if (dirParallel != nullptr) {
|
|
if (dirTaskloop == nullptr && dirWorksharing == nullptr) {
|
|
dirParallel->clauses.push_back(node);
|
|
applied = true;
|
|
} else {
|
|
// [5.2:340:15]
|
|
auto *shared = makeClause(
|
|
llvm::omp::Clause::OMPC_shared,
|
|
tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/clause.v});
|
|
dirParallel->clauses.push_back(shared);
|
|
}
|
|
}
|
|
|
|
// [5.2:340:12]
|
|
auto inLastprivate = [&](const ObjectTy &object) {
|
|
if (ClauseSet *set = findClausesWith(object)) {
|
|
return llvm::find_if(*set, [](const ClauseTy *c) {
|
|
return c->id == llvm::omp::Clause::OMPC_lastprivate;
|
|
}) != set->end();
|
|
}
|
|
return false;
|
|
};
|
|
|
|
auto dirTarget = findDirective(llvm::omp::OMPD_target);
|
|
if (dirTarget != nullptr) {
|
|
tomp::ObjectListT<IdTy, ExprTy> objects;
|
|
llvm::copy_if(
|
|
clause.v, std::back_inserter(objects), [&](const ObjectTy &object) {
|
|
return !inLastprivate(object) && !mapBases.count(object.id());
|
|
});
|
|
if (!objects.empty()) {
|
|
auto *firstp = makeClause(
|
|
llvm::omp::Clause::OMPC_firstprivate,
|
|
tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>{/*List=*/objects});
|
|
dirTarget->clauses.push_back(firstp);
|
|
applied = true;
|
|
}
|
|
}
|
|
|
|
// "task" is not handled by any of the cases above.
|
|
if (auto dirTask = findDirective(llvm::omp::OMPD_task)) {
|
|
dirTask->clauses.push_back(node);
|
|
applied = true;
|
|
}
|
|
|
|
return applied;
|
|
}
|
|
|
|
// LASTPRIVATE
|
|
// [5.2:115:7-8]
|
|
// Directives: distribute, do, for, loop, sections, simd, taskloop
|
|
//
|
|
// [5.2:340:21-30]
|
|
// (21) The effect of the lastprivate clause is as if it is applied to all leaf
|
|
// constructs that permit the clause.
|
|
// (22) If the parallel construct is among the constituent constructs and the
|
|
// list item is not also specified in the firstprivate clause, then the effect
|
|
// of the lastprivate clause is as if the shared clause with the same list item
|
|
// is applied to the parallel construct.
|
|
// (24) If the teams construct is among the constituent constructs and the list
|
|
// item is not also specified in the firstprivate clause, then the effect of the
|
|
// lastprivate clause is as if the shared clause with the same list item is
|
|
// applied to the teams construct.
|
|
// (27) If the target construct is among the constituent constructs and the list
|
|
// item is not the base variable or base pointer of a list item that appears in
|
|
// a map clause, the effect of the lastprivate clause is as if the same list
|
|
// item appears in a map clause with a map-type of tofrom.
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
bool applied = false;
|
|
|
|
// [5.2:340:21]
|
|
applied = applyToAll(node);
|
|
if (!applied)
|
|
return false;
|
|
|
|
auto inFirstprivate = [&](const ObjectTy &object) {
|
|
if (ClauseSet *set = findClausesWith(object)) {
|
|
return llvm::find_if(*set, [](const ClauseTy *c) {
|
|
return c->id == llvm::omp::Clause::OMPC_firstprivate;
|
|
}) != set->end();
|
|
}
|
|
return false;
|
|
};
|
|
|
|
auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
|
|
|
|
// Prepare list of objects that could end up in a "shared" clause.
|
|
tomp::ObjectListT<IdTy, ExprTy> sharedObjects;
|
|
llvm::copy_if(
|
|
objects, std::back_inserter(sharedObjects),
|
|
[&](const ObjectTy &object) { return !inFirstprivate(object); });
|
|
|
|
if (!sharedObjects.empty()) {
|
|
// [5.2:340:22]
|
|
if (auto dirParallel = findDirective(llvm::omp::OMPD_parallel)) {
|
|
auto *shared = makeClause(
|
|
llvm::omp::Clause::OMPC_shared,
|
|
tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
|
|
dirParallel->clauses.push_back(shared);
|
|
applied = true;
|
|
}
|
|
|
|
// [5.2:340:24]
|
|
if (auto dirTeams = findDirective(llvm::omp::OMPD_teams)) {
|
|
auto *shared = makeClause(
|
|
llvm::omp::Clause::OMPC_shared,
|
|
tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
|
|
dirTeams->clauses.push_back(shared);
|
|
applied = true;
|
|
}
|
|
}
|
|
|
|
// [5.2:340:27]
|
|
if (auto dirTarget = findDirective(llvm::omp::OMPD_target)) {
|
|
tomp::ObjectListT<IdTy, ExprTy> tofrom;
|
|
llvm::copy_if(
|
|
objects, std::back_inserter(tofrom),
|
|
[&](const ObjectTy &object) { return !mapBases.count(object.id()); });
|
|
|
|
if (!tofrom.empty()) {
|
|
using MapType =
|
|
typename tomp::clause::MapT<TypeTy, IdTy, ExprTy>::MapType;
|
|
auto *map =
|
|
makeClause(llvm::omp::Clause::OMPC_map,
|
|
tomp::clause::MapT<TypeTy, IdTy, ExprTy>{
|
|
{/*MapType=*/MapType::Tofrom,
|
|
/*MapTypeModifier=*/std::nullopt,
|
|
/*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
|
|
/*LocatorList=*/std::move(tofrom)}});
|
|
dirTarget->clauses.push_back(map);
|
|
applied = true;
|
|
}
|
|
}
|
|
|
|
return applied;
|
|
}
|
|
|
|
// SHARED
|
|
// [5.2:110:5-6]
|
|
// Directives: parallel, task, taskloop, teams
|
|
//
|
|
// [5.2:340:31-32]
|
|
// (31) The effect of the shared, default, thread_limit, or order clause is as
|
|
// if it is applied to all leaf constructs that permit the clause.
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::SharedT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
// [5.2:340:31]
|
|
return applyToAll(node);
|
|
}
|
|
|
|
// DEFAULT
|
|
// [5.2:109:5-6]
|
|
// Directives: parallel, task, taskloop, teams
|
|
//
|
|
// [5.2:340:31-32]
|
|
// (31) The effect of the shared, default, thread_limit, or order clause is as
|
|
// if it is applied to all leaf constructs that permit the clause.
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::DefaultT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
// [5.2:340:31]
|
|
return applyToAll(node);
|
|
}
|
|
|
|
// THREAD_LIMIT
|
|
// [5.2:277:14-15]
|
|
// Directives: target, teams
|
|
//
|
|
// [5.2:340:31-32]
|
|
// (31) The effect of the shared, default, thread_limit, or order clause is as
|
|
// if it is applied to all leaf constructs that permit the clause.
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
// [5.2:340:31]
|
|
return applyToAll(node);
|
|
}
|
|
|
|
// ORDER
|
|
// [5.2:234:3-4]
|
|
// Directives: distribute, do, for, loop, simd
|
|
//
|
|
// [5.2:340:31-32]
|
|
// (31) The effect of the shared, default, thread_limit, or order clause is as
|
|
// if it is applied to all leaf constructs that permit the clause.
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::OrderT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
// [5.2:340:31]
|
|
return applyToAll(node);
|
|
}
|
|
|
|
// ALLOCATE
|
|
// [5.2:178:7-9]
|
|
// Directives: allocators, distribute, do, for, parallel, scope, sections,
|
|
// single, target, task, taskgroup, taskloop, teams
|
|
//
|
|
// [5.2:340:33-35]
|
|
// (33) The effect of the allocate clause is as if it is applied to all leaf
|
|
// constructs that permit the clause and to which a data-sharing attribute
|
|
// clause that may create a private copy of the same list item is applied.
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::AllocateT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
// This one needs to be applied at the end, once we know which clauses are
|
|
// assigned to which leaf constructs.
|
|
|
|
// [5.2:340:33]
|
|
auto canMakePrivateCopy = [](llvm::omp::Clause id) {
|
|
switch (id) {
|
|
// Clauses with "privatization" property:
|
|
case llvm::omp::Clause::OMPC_firstprivate:
|
|
case llvm::omp::Clause::OMPC_in_reduction:
|
|
case llvm::omp::Clause::OMPC_lastprivate:
|
|
case llvm::omp::Clause::OMPC_linear:
|
|
case llvm::omp::Clause::OMPC_private:
|
|
case llvm::omp::Clause::OMPC_reduction:
|
|
case llvm::omp::Clause::OMPC_task_reduction:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
};
|
|
|
|
bool applied = applyIf(node, [&](const auto &leaf) {
|
|
return llvm::any_of(leaf.clauses, [&](const ClauseTy *n) {
|
|
return canMakePrivateCopy(n->id);
|
|
});
|
|
});
|
|
|
|
return applied;
|
|
}
|
|
|
|
// REDUCTION
|
|
// [5.2:134:17-18]
|
|
// Directives: do, for, loop, parallel, scope, sections, simd, taskloop, teams
|
|
//
|
|
// [5.2:340:36-37], [5.2:341:1-13]
|
|
// (36) The effect of the reduction clause is as if it is applied to all leaf
|
|
// constructs that permit the clause, except for the following constructs:
|
|
// (1) The parallel construct, when combined with the sections,
|
|
// worksharing-loop, loop, or taskloop construct; and
|
|
// (3) The teams construct, when combined with the loop construct.
|
|
// (4) For the parallel and teams constructs above, the effect of the reduction
|
|
// clause instead is as if each list item or, for any list item that is an array
|
|
// item, its corresponding base array or base pointer appears in a shared clause
|
|
// for the construct.
|
|
// (6) If the task reduction-modifier is specified, the effect is as if it only
|
|
// modifies the behavior of the reduction clause on the innermost leaf construct
|
|
// that accepts the modifier (see Section 5.5.8).
|
|
// (8) If the inscan reduction-modifier is specified, the effect is as if it
|
|
// modifies the behavior of the reduction clause on all constructs of the
|
|
// combined construct to which the clause is applied and that accept the
|
|
// modifier.
|
|
// (10) If a list item in a reduction clause on a combined target construct does
|
|
// not have the same base variable or base pointer as a list item in a map
|
|
// clause on the construct, then the effect is as if the list item in the
|
|
// reduction clause appears as a list item in a map clause with a map-type of
|
|
// tofrom.
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::ReductionT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
using ReductionTy = tomp::clause::ReductionT<TypeTy, IdTy, ExprTy>;
|
|
|
|
// [5.2:340:36], [5.2:341:1], [5.2:341:3]
|
|
bool applyToParallel = true, applyToTeams = true;
|
|
|
|
auto dirParallel = findDirective(llvm::omp::Directive::OMPD_parallel);
|
|
if (dirParallel) {
|
|
auto exclusions = llvm::concat<const llvm::omp::Directive>(
|
|
getWorksharingLoop(), tomp::ListT<llvm::omp::Directive>{
|
|
llvm::omp::Directive::OMPD_loop,
|
|
llvm::omp::Directive::OMPD_sections,
|
|
llvm::omp::Directive::OMPD_taskloop,
|
|
});
|
|
auto present = [&](llvm::omp::Directive id) {
|
|
return findDirective(id) != nullptr;
|
|
};
|
|
|
|
if (llvm::any_of(exclusions, present))
|
|
applyToParallel = false;
|
|
}
|
|
|
|
auto dirTeams = findDirective(llvm::omp::Directive::OMPD_teams);
|
|
if (dirTeams) {
|
|
// The only exclusion is OMPD_loop.
|
|
if (findDirective(llvm::omp::Directive::OMPD_loop))
|
|
applyToTeams = false;
|
|
}
|
|
|
|
using ReductionModifier = typename ReductionTy::ReductionModifier;
|
|
using ReductionIdentifiers = typename ReductionTy::ReductionIdentifiers;
|
|
|
|
auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
|
|
auto &modifier = std::get<std::optional<ReductionModifier>>(clause.t);
|
|
|
|
// Apply the reduction clause first to all directives according to the spec.
|
|
// If the reduction was applied at least once, proceed with the data sharing
|
|
// side-effects.
|
|
bool applied = false;
|
|
|
|
// [5.2:341:6], [5.2:341:8]
|
|
auto isValidModifier = [](llvm::omp::Directive dir, ReductionModifier mod,
|
|
bool alreadyApplied) {
|
|
switch (mod) {
|
|
case ReductionModifier::Inscan:
|
|
// According to [5.2:135:11-13], "inscan" only applies to
|
|
// worksharing-loop, worksharing-loop-simd, or "simd" constructs.
|
|
return dir == llvm::omp::Directive::OMPD_simd ||
|
|
llvm::is_contained(getWorksharingLoop(), dir);
|
|
case ReductionModifier::Task:
|
|
if (alreadyApplied)
|
|
return false;
|
|
// According to [5.2:135:16-18], "task" only applies to "parallel" and
|
|
// worksharing constructs.
|
|
return dir == llvm::omp::Directive::OMPD_parallel ||
|
|
llvm::is_contained(getWorksharing(), dir);
|
|
case ReductionModifier::Default:
|
|
return true;
|
|
}
|
|
llvm_unreachable("Unexpected modifier");
|
|
};
|
|
|
|
auto *unmodified = makeClause(
|
|
llvm::omp::Clause::OMPC_reduction,
|
|
ReductionTy{
|
|
{/*ReductionModifier=*/std::nullopt,
|
|
/*ReductionIdentifiers=*/std::get<ReductionIdentifiers>(clause.t),
|
|
/*List=*/objects}});
|
|
|
|
ReductionModifier effective =
|
|
modifier.has_value() ? *modifier : ReductionModifier::Default;
|
|
bool effectiveApplied = false;
|
|
// Walk over the leaf constructs starting from the innermost, and apply
|
|
// the clause as required by the spec.
|
|
for (auto &leaf : llvm::reverse(leafs)) {
|
|
if (!llvm::omp::isAllowedClauseForDirective(leaf.id, node->id, version))
|
|
continue;
|
|
if (!applyToParallel && &leaf == dirParallel)
|
|
continue;
|
|
if (!applyToTeams && &leaf == dirTeams)
|
|
continue;
|
|
// Some form of the clause will be applied past this point.
|
|
if (isValidModifier(leaf.id, effective, effectiveApplied)) {
|
|
// Apply clause with modifier.
|
|
leaf.clauses.push_back(node);
|
|
effectiveApplied = true;
|
|
} else {
|
|
// Apply clause without modifier.
|
|
leaf.clauses.push_back(unmodified);
|
|
}
|
|
// The modifier must be applied to some construct.
|
|
applied = effectiveApplied;
|
|
}
|
|
|
|
if (!applied)
|
|
return false;
|
|
|
|
tomp::ObjectListT<IdTy, ExprTy> sharedObjects;
|
|
llvm::transform(objects, std::back_inserter(sharedObjects),
|
|
[&](const ObjectTy &object) {
|
|
auto maybeBase = helper.getBaseObject(object);
|
|
return maybeBase ? *maybeBase : object;
|
|
});
|
|
|
|
// [5.2:341:4]
|
|
if (!sharedObjects.empty()) {
|
|
if (dirParallel && !applyToParallel) {
|
|
auto *shared = makeClause(
|
|
llvm::omp::Clause::OMPC_shared,
|
|
tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
|
|
dirParallel->clauses.push_back(shared);
|
|
}
|
|
if (dirTeams && !applyToTeams) {
|
|
auto *shared = makeClause(
|
|
llvm::omp::Clause::OMPC_shared,
|
|
tomp::clause::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/sharedObjects});
|
|
dirTeams->clauses.push_back(shared);
|
|
}
|
|
}
|
|
|
|
// [5.2:341:10]
|
|
auto dirTarget = findDirective(llvm::omp::Directive::OMPD_target);
|
|
if (dirTarget && leafs.size() > 1) {
|
|
tomp::ObjectListT<IdTy, ExprTy> tofrom;
|
|
llvm::copy_if(objects, std::back_inserter(tofrom),
|
|
[&](const ObjectTy &object) {
|
|
if (auto maybeBase = helper.getBaseObject(object))
|
|
return !mapBases.count(maybeBase->id());
|
|
return !mapBases.count(object.id()); // XXX is this ok?
|
|
});
|
|
if (!tofrom.empty()) {
|
|
using MapType =
|
|
typename tomp::clause::MapT<TypeTy, IdTy, ExprTy>::MapType;
|
|
auto *map = makeClause(
|
|
llvm::omp::Clause::OMPC_map,
|
|
tomp::clause::MapT<TypeTy, IdTy, ExprTy>{
|
|
{/*MapType=*/MapType::Tofrom, /*MapTypeModifier=*/std::nullopt,
|
|
/*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
|
|
/*LocatorList=*/std::move(tofrom)}});
|
|
|
|
dirTarget->clauses.push_back(map);
|
|
applied = true;
|
|
}
|
|
}
|
|
|
|
return applied;
|
|
}
|
|
|
|
// IF
|
|
// [5.2:72:7-9]
|
|
// Directives: cancel, parallel, simd, target, target data, target enter data,
|
|
// target exit data, target update, task, taskloop
|
|
//
|
|
// [5.2:72:15-18]
|
|
// (15) For combined or composite constructs, the if clause only applies to the
|
|
// semantics of the construct named in the directive-name-modifier.
|
|
// (16) For a combined or composite construct, if no directive-name-modifier is
|
|
// specified then the if clause applies to all constituent constructs to which
|
|
// an if clause can apply.
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::IfT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
using DirectiveNameModifier =
|
|
typename clause::IfT<TypeTy, IdTy, ExprTy>::DirectiveNameModifier;
|
|
using IfExpression = typename clause::IfT<TypeTy, IdTy, ExprTy>::IfExpression;
|
|
auto &modifier = std::get<std::optional<DirectiveNameModifier>>(clause.t);
|
|
|
|
if (modifier) {
|
|
llvm::omp::Directive dirId = *modifier;
|
|
auto *unmodified =
|
|
makeClause(llvm::omp::Clause::OMPC_if,
|
|
tomp::clause::IfT<TypeTy, IdTy, ExprTy>{
|
|
{/*DirectiveNameModifier=*/std::nullopt,
|
|
/*IfExpression=*/std::get<IfExpression>(clause.t)}});
|
|
|
|
if (auto *hasDir = findDirective(dirId)) {
|
|
hasDir->clauses.push_back(unmodified);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return applyToAll(node);
|
|
}
|
|
|
|
// LINEAR
|
|
// [5.2:118:1-2]
|
|
// Directives: declare simd, do, for, simd
|
|
//
|
|
// [5.2:341:15-22]
|
|
// (15.1) The effect of the linear clause is as if it is applied to the
|
|
// innermost leaf construct.
|
|
// (15.2) Additionally, if the list item is not the iteration variable of a simd
|
|
// or worksharing-loop SIMD construct, the effect on the outer leaf constructs
|
|
// is as if the list item was specified in firstprivate and lastprivate clauses
|
|
// on the combined or composite construct, with the rules specified above
|
|
// applied.
|
|
// (19) If a list item of the linear clause is the iteration variable of a simd
|
|
// or worksharing-loop SIMD construct and it is not declared in the construct,
|
|
// the effect on the outer leaf constructs is as if the list item was specified
|
|
// in a lastprivate clause on the combined or composite construct with the rules
|
|
// specified above applied.
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::LinearT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
// [5.2:341:15.1]
|
|
if (!applyToInnermost(node))
|
|
return false;
|
|
|
|
// [5.2:341:15.2], [5.2:341:19]
|
|
auto dirSimd = findDirective(llvm::omp::Directive::OMPD_simd);
|
|
std::optional<ObjectTy> iterVar = helper.getLoopIterVar();
|
|
const auto &objects = std::get<tomp::ObjectListT<IdTy, ExprTy>>(clause.t);
|
|
|
|
// Lists of objects that will be used to construct "firstprivate" and
|
|
// "lastprivate" clauses.
|
|
tomp::ObjectListT<IdTy, ExprTy> first, last;
|
|
|
|
for (const ObjectTy &object : objects) {
|
|
last.push_back(object);
|
|
if (!dirSimd || !iterVar || object.id() != iterVar->id())
|
|
first.push_back(object);
|
|
}
|
|
|
|
if (!first.empty()) {
|
|
auto *firstp = makeClause(
|
|
llvm::omp::Clause::OMPC_firstprivate,
|
|
tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>{/*List=*/first});
|
|
nodes.push_back(firstp); // Appending to the main clause list.
|
|
}
|
|
if (!last.empty()) {
|
|
auto *lastp =
|
|
makeClause(llvm::omp::Clause::OMPC_lastprivate,
|
|
tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy>{
|
|
{/*LastprivateModifier=*/std::nullopt, /*List=*/last}});
|
|
nodes.push_back(lastp); // Appending to the main clause list.
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// NOWAIT
|
|
// [5.2:308:11-13]
|
|
// Directives: dispatch, do, for, interop, scope, sections, single, target,
|
|
// target enter data, target exit data, target update, taskwait, workshare
|
|
//
|
|
// [5.2:341:23]
|
|
// (23) The effect of the nowait clause is as if it is applied to the outermost
|
|
// leaf construct that permits it.
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::NowaitT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
return applyToOutermost(node);
|
|
}
|
|
|
|
template <typename C, typename H>
|
|
bool ConstructDecompositionT<C, H>::applyClause(
|
|
const tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy> &clause,
|
|
const ClauseTy *node) {
|
|
return applyToAll(node);
|
|
}
|
|
|
|
template <typename C, typename H> bool ConstructDecompositionT<C, H>::split() {
|
|
bool success = true;
|
|
|
|
auto isImplicit = [this](const ClauseTy *node) {
|
|
return llvm::any_of(
|
|
implicit, [node](const ClauseTy &clause) { return &clause == node; });
|
|
};
|
|
|
|
for (llvm::omp::Directive leaf :
|
|
llvm::omp::getLeafConstructsOrSelf(construct))
|
|
leafs.push_back(LeafReprInternal{leaf, /*clauses=*/{}});
|
|
|
|
for (const ClauseTy *node : nodes)
|
|
addClauseSymsToMap(*node, node);
|
|
|
|
// First we need to apply LINEAR, because it can generate additional
|
|
// "firstprivate" and "lastprivate" clauses that apply to the combined/
|
|
// composite construct.
|
|
// Collect them separately, because they may modify the clause list.
|
|
llvm::SmallVector<const ClauseTy *> linears;
|
|
for (const ClauseTy *node : nodes) {
|
|
if (node->id == llvm::omp::Clause::OMPC_linear)
|
|
linears.push_back(node);
|
|
}
|
|
for (const auto *node : linears) {
|
|
success = success &&
|
|
applyClause(std::get<tomp::clause::LinearT<TypeTy, IdTy, ExprTy>>(
|
|
node->u),
|
|
node);
|
|
}
|
|
|
|
// "allocate" clauses need to be applied last since they need to see
|
|
// which directives have data-privatizing clauses.
|
|
auto skip = [](const ClauseTy *node) {
|
|
switch (node->id) {
|
|
case llvm::omp::Clause::OMPC_allocate:
|
|
case llvm::omp::Clause::OMPC_linear:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Apply (almost) all clauses.
|
|
for (const ClauseTy *node : nodes) {
|
|
if (skip(node))
|
|
continue;
|
|
bool result =
|
|
std::visit([&](auto &&s) { return applyClause(s, node); }, node->u);
|
|
if (!isImplicit(node))
|
|
success = success && result;
|
|
}
|
|
|
|
// Apply "allocate".
|
|
for (const ClauseTy *node : nodes) {
|
|
if (node->id != llvm::omp::Clause::OMPC_allocate)
|
|
continue;
|
|
success =
|
|
success &&
|
|
std::visit([&](auto &&s) { return applyClause(s, node); }, node->u);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
} // namespace tomp
|
|
|
|
#endif // LLVM_FRONTEND_OPENMP_CONSTRUCTDECOMPOSITIONT_H
|