LLVM 20.0.0git
ConstructCompositionT.h
Go to the documentation of this file.
1//===- ConstructCompositionT.h -- Composing compound constructs -----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// Given a list of leaf construct, each with a set of clauses, generate the
9// compound construct whose leaf constructs are the given list, and whose clause
10// list is the merged lists of individual leaf clauses.
11//
12// *** At the moment it assumes that the individual constructs and their clauses
13// *** are a subset of those created by splitting a valid compound construct.
14//===----------------------------------------------------------------------===//
15#ifndef LLVM_FRONTEND_OPENMP_CONSTRUCTCOMPOSITIONT_H
16#define LLVM_FRONTEND_OPENMP_CONSTRUCTCOMPOSITIONT_H
17
18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/ADT/BitVector.h"
20#include "llvm/ADT/STLExtras.h"
24
25#include <iterator>
26#include <optional>
27#include <tuple>
28#include <unordered_map>
29#include <unordered_set>
30#include <utility>
31
32namespace tomp {
33template <typename ClauseType> struct ConstructCompositionT {
34 using ClauseTy = ClauseType;
35
36 using TypeTy = typename ClauseTy::TypeTy;
37 using IdTy = typename ClauseTy::IdTy;
38 using ExprTy = typename ClauseTy::ExprTy;
39
42
44
45private:
46 // Use an ordered container, since we beed to maintain the order in which
47 // clauses are added to it. This is to avoid non-deterministic output.
49
50 enum class Presence {
51 All, // Clause is preesnt on all leaf constructs that allow it.
52 Some, // Clause is present on some, but not on all constructs.
53 None, // Clause is absent on all constructs.
54 };
55
56 template <typename S>
57 ClauseTy makeClause(llvm::omp::Clause clauseId, S &&specific) {
58 return typename ClauseTy::BaseT{clauseId, std::move(specific)};
59 }
60
61 llvm::omp::Directive
62 makeCompound(llvm::ArrayRef<DirectiveWithClauses<ClauseTy>> parts);
63
64 Presence checkPresence(llvm::omp::Clause clauseId);
65
66 // There are clauses that need special handling:
67 // 1. "if": the "directive-name-modifier" on the merged clause may need
68 // to be set appropriately.
69 // 2. "reduction": implies "privateness" of all objects (incompatible
70 // with "shared"); there are rules for merging modifiers
71 void mergeIf();
72 void mergeReduction();
73 void mergeDSA();
74
75 uint32_t version;
77
78 // clause id -> set of leaf constructs that contain it
79 std::unordered_map<llvm::omp::Clause, llvm::BitVector> clausePresence;
80 // clause id -> set of instances of that clause
81 std::unordered_map<llvm::omp::Clause, ClauseSet> clauseSets;
82};
83
84template <typename ClauseTy>
87
88template <typename C>
91 : version(version), leafs(leafs) {
92 // Merge the list of constructs with clauses into a compound construct
93 // with a single list of clauses.
94 // The intended use of this function is in splitting compound constructs,
95 // while preserving composite constituent constructs:
96 // Step 1: split compound construct into leaf constructs.
97 // Step 2: identify composite sub-construct, and merge the constituent leafs.
98 //
99 // *** At the moment it assumes that the individual constructs and their
100 // *** clauses are a subset of those created by splitting a valid compound
101 // *** construct.
102 //
103 // 1. Deduplicate clauses
104 // - exact duplicates: e.g. shared(x) shared(x) -> shared(x)
105 // - special cases of clauses differing in modifier:
106 // (a) reduction: inscan + (none|default) = inscan
107 // (b) reduction: task + (none|default) = task
108 // (c) combine repeated "if" clauses if possible
109 // 2. Merge DSA clauses: e.g. private(x) private(y) -> private(x, y).
110 // 3. Resolve potential DSA conflicts (typically due to implied clauses).
111
112 if (leafs.empty())
113 return;
114
115 merged.id = makeCompound(leafs);
116
117 // Populate the two maps:
118 for (const auto &[index, leaf] : llvm::enumerate(leafs)) {
119 for (const auto &clause : leaf.clauses) {
120 // Update clausePresence.
121 auto &pset = clausePresence[clause.id];
122 if (pset.size() < leafs.size())
123 pset.resize(leafs.size());
124 pset.set(index);
125 // Update clauseSets.
126 ClauseSet &cset = clauseSets[clause.id];
127 if (!llvm::is_contained(cset, clause))
128 cset.push_back(clause);
129 }
130 }
131
132 mergeIf();
133 mergeReduction();
134 mergeDSA();
135
136 // For the rest of the clauses, just copy them.
137 for (auto &[id, clauses] : clauseSets) {
138 // Skip clauses we've already dealt with.
139 switch (id) {
140 case llvm::omp::Clause::OMPC_if:
141 case llvm::omp::Clause::OMPC_reduction:
142 case llvm::omp::Clause::OMPC_shared:
143 case llvm::omp::Clause::OMPC_private:
144 case llvm::omp::Clause::OMPC_firstprivate:
145 case llvm::omp::Clause::OMPC_lastprivate:
146 continue;
147 default:
148 break;
149 }
151 }
152}
153
154template <typename C>
155llvm::omp::Directive ConstructCompositionT<C>::makeCompound(
158 llvm::transform(parts, std::back_inserter(dirIds),
159 [](auto &&dwc) { return dwc.id; });
160
161 return llvm::omp::getCompoundConstruct(dirIds);
162}
163
164template <typename C>
165auto ConstructCompositionT<C>::checkPresence(llvm::omp::Clause clauseId)
166 -> Presence {
167 auto found = clausePresence.find(clauseId);
168 if (found == clausePresence.end())
169 return Presence::None;
170
171 bool OnAll = true, OnNone = true;
172 for (const auto &[index, leaf] : llvm::enumerate(leafs)) {
173 if (!llvm::omp::isAllowedClauseForDirective(leaf.id, clauseId, version))
174 continue;
175
176 if (found->second.test(index))
177 OnNone = false;
178 else
179 OnAll = false;
180 }
181
182 if (OnNone)
183 return Presence::None;
184 if (OnAll)
185 return Presence::All;
186 return Presence::Some;
187}
188
189template <typename C> void ConstructCompositionT<C>::mergeIf() {
191 // Deal with the "if" clauses. If it's on all leafs that allow it, then it
192 // will apply to the compound construct. Otherwise it will apply to the
193 // single (assumed) leaf construct.
194 // This assumes that the "if" clauses have the same expression.
195 Presence presence = checkPresence(llvm::omp::Clause::OMPC_if);
196 if (presence == Presence::None)
197 return;
198
199 const ClauseTy &some = *clauseSets[llvm::omp::Clause::OMPC_if].begin();
200 const auto &someIf = std::get<IfTy>(some.u);
201
202 if (presence == Presence::All) {
203 // Create "if" without "directive-name-modifier".
204 merged.clauses.emplace_back(
205 makeClause(llvm::omp::Clause::OMPC_if,
206 IfTy{{/*DirectiveNameModifier=*/std::nullopt,
207 /*IfExpression=*/std::get<typename IfTy::IfExpression>(
208 someIf.t)}}));
209 } else {
210 // Find out where it's present and create "if" with the corresponding
211 // "directive-name-modifier".
212 int Idx = clausePresence[llvm::omp::Clause::OMPC_if].find_first();
213 assert(Idx >= 0);
214 merged.clauses.emplace_back(
215 makeClause(llvm::omp::Clause::OMPC_if,
216 IfTy{{/*DirectiveNameModifier=*/leafs[Idx].id,
217 /*IfExpression=*/std::get<typename IfTy::IfExpression>(
218 someIf.t)}}));
219 }
220}
221
222template <typename C> void ConstructCompositionT<C>::mergeReduction() {
223 Presence presence = checkPresence(llvm::omp::Clause::OMPC_reduction);
224 if (presence == Presence::None)
225 return;
226
228 using ModifierTy = typename ReductionTy::ReductionModifier;
229 using IdentifiersTy = typename ReductionTy::ReductionIdentifiers;
230 using ListTy = typename ReductionTy::List;
231 // There are exceptions on which constructs "reduction" may appear
232 // (specifically "parallel", and "teams"). Assume that if "reduction"
233 // is present, it can be applied to the compound construct.
234
235 // What's left is to see if there are any modifiers present. Again,
236 // assume that there are no conflicting modifiers.
237 // There can be, however, multiple reductions on different objects.
238 auto equal = [](const ClauseTy &red1, const ClauseTy &red2) {
239 // Extract actual reductions.
240 const auto r1 = std::get<ReductionTy>(red1.u);
241 const auto r2 = std::get<ReductionTy>(red2.u);
242 // Compare everything except modifiers.
243 if (std::get<IdentifiersTy>(r1.t) != std::get<IdentifiersTy>(r2.t))
244 return false;
245 if (std::get<ListTy>(r1.t) != std::get<ListTy>(r2.t))
246 return false;
247 return true;
248 };
249
250 auto getModifier = [](const ClauseTy &clause) {
251 const ReductionTy &red = std::get<ReductionTy>(clause.u);
252 return std::get<std::optional<ModifierTy>>(red.t);
253 };
254
255 const ClauseSet &reductions = clauseSets[llvm::omp::Clause::OMPC_reduction];
256 std::unordered_set<const ClauseTy *> visited;
257 while (reductions.size() != visited.size()) {
258 typename ClauseSet::const_iterator first;
259
260 // Find first non-visited reduction.
261 for (first = reductions.begin(); first != reductions.end(); ++first) {
262 if (visited.count(&*first))
263 continue;
264 visited.insert(&*first);
265 break;
266 }
267
268 std::optional<ModifierTy> modifier = getModifier(*first);
269
270 // Visit all other reductions that are "equal" (with respect to the
271 // definition above) to "first". Collect modifiers.
272 for (auto iter = std::next(first); iter != reductions.end(); ++iter) {
273 if (!equal(*first, *iter))
274 continue;
275 visited.insert(&*iter);
276 if (!modifier || *modifier == ModifierTy::Default)
277 modifier = getModifier(*iter);
278 }
279
280 const auto &firstRed = std::get<ReductionTy>(first->u);
281 merged.clauses.emplace_back(makeClause(
282 llvm::omp::Clause::OMPC_reduction,
283 ReductionTy{
284 {/*ReductionModifier=*/modifier,
285 /*ReductionIdentifiers=*/std::get<IdentifiersTy>(firstRed.t),
286 /*List=*/std::get<ListTy>(firstRed.t)}}));
287 }
288}
289
290template <typename C> void ConstructCompositionT<C>::mergeDSA() {
291 using ObjectTy = tomp::type::ObjectT<IdTy, ExprTy>;
292
293 // Resolve data-sharing attributes.
294 enum DSA : int {
295 None = 0,
296 Shared = 1 << 0,
297 Private = 1 << 1,
298 FirstPrivate = 1 << 2,
299 LastPrivate = 1 << 3,
300 LastPrivateConditional = 1 << 4,
301 };
302
303 // Use ordered containers to avoid non-deterministic output.
305
306 auto getDsa = [&](const ObjectTy &object) -> std::pair<ObjectTy, int> & {
307 auto found = llvm::find_if(objectDsa, [&](std::pair<ObjectTy, int> &p) {
308 return p.first.id() == object.id();
309 });
310 if (found != objectDsa.end())
311 return *found;
312 return objectDsa.emplace_back(object, DSA::None);
313 };
314
319
320 // Visit clauses that affect DSA.
321 for (auto &clause : clauseSets[llvm::omp::Clause::OMPC_shared]) {
322 for (auto &object : std::get<SharedTy>(clause.u).v)
323 getDsa(object).second |= DSA::Shared;
324 }
325
326 for (auto &clause : clauseSets[llvm::omp::Clause::OMPC_private]) {
327 for (auto &object : std::get<PrivateTy>(clause.u).v)
328 getDsa(object).second |= DSA::Private;
329 }
330
331 for (auto &clause : clauseSets[llvm::omp::Clause::OMPC_firstprivate]) {
332 for (auto &object : std::get<FirstprivateTy>(clause.u).v)
333 getDsa(object).second |= DSA::FirstPrivate;
334 }
335
336 for (auto &clause : clauseSets[llvm::omp::Clause::OMPC_lastprivate]) {
337 using ModifierTy = typename LastprivateTy::LastprivateModifier;
338 using ListTy = typename LastprivateTy::List;
339 const auto &lastp = std::get<LastprivateTy>(clause.u);
340 for (auto &object : std::get<ListTy>(lastp.t)) {
341 auto &mod = std::get<std::optional<ModifierTy>>(lastp.t);
342 if (mod && *mod == ModifierTy::Conditional) {
343 getDsa(object).second |= DSA::LastPrivateConditional;
344 } else {
345 getDsa(object).second |= DSA::LastPrivate;
346 }
347 }
348 }
349
350 // Check other privatizing clauses as well, clear "shared" if set.
351 for (auto &clause : clauseSets[llvm::omp::Clause::OMPC_in_reduction]) {
353 using ListTy = typename InReductionTy::List;
354 for (auto &object : std::get<ListTy>(std::get<InReductionTy>(clause.u).t))
355 getDsa(object).second &= ~DSA::Shared;
356 }
357 for (auto &clause : clauseSets[llvm::omp::Clause::OMPC_linear]) {
359 using ListTy = typename LinearTy::List;
360 for (auto &object : std::get<ListTy>(std::get<LinearTy>(clause.u).t))
361 getDsa(object).second &= ~DSA::Shared;
362 }
363 for (auto &clause : clauseSets[llvm::omp::Clause::OMPC_reduction]) {
365 using ListTy = typename ReductionTy::List;
366 for (auto &object : std::get<ListTy>(std::get<ReductionTy>(clause.u).t))
367 getDsa(object).second &= ~DSA::Shared;
368 }
369 for (auto &clause : clauseSets[llvm::omp::Clause::OMPC_task_reduction]) {
371 using ListTy = typename TaskReductionTy::List;
372 for (auto &object : std::get<ListTy>(std::get<TaskReductionTy>(clause.u).t))
373 getDsa(object).second &= ~DSA::Shared;
374 }
375
376 tomp::ListT<ObjectTy> privateObj, sharedObj, firstpObj, lastpObj, lastpcObj;
377 for (auto &[object, dsa] : objectDsa) {
378 if (dsa &
379 (DSA::FirstPrivate | DSA::LastPrivate | DSA::LastPrivateConditional)) {
380 if (dsa & DSA::FirstPrivate)
381 firstpObj.push_back(object); // no else
382 if (dsa & DSA::LastPrivateConditional)
383 lastpcObj.push_back(object);
384 else if (dsa & DSA::LastPrivate)
385 lastpObj.push_back(object);
386 } else if (dsa & DSA::Private) {
387 privateObj.push_back(object);
388 } else if (dsa & DSA::Shared) {
389 sharedObj.push_back(object);
390 }
391 }
392
393 // Materialize each clause.
394 if (!privateObj.empty()) {
395 merged.clauses.emplace_back(
396 makeClause(llvm::omp::Clause::OMPC_private,
397 PrivateTy{/*List=*/std::move(privateObj)}));
398 }
399 if (!sharedObj.empty()) {
400 merged.clauses.emplace_back(
401 makeClause(llvm::omp::Clause::OMPC_shared,
402 SharedTy{/*List=*/std::move(sharedObj)}));
403 }
404 if (!firstpObj.empty()) {
405 merged.clauses.emplace_back(
406 makeClause(llvm::omp::Clause::OMPC_firstprivate,
407 FirstprivateTy{/*List=*/std::move(firstpObj)}));
408 }
409 if (!lastpObj.empty()) {
410 merged.clauses.emplace_back(
411 makeClause(llvm::omp::Clause::OMPC_lastprivate,
412 LastprivateTy{{/*LastprivateModifier=*/std::nullopt,
413 /*List=*/std::move(lastpObj)}}));
414 }
415 if (!lastpcObj.empty()) {
416 auto conditional = LastprivateTy::LastprivateModifier::Conditional;
417 merged.clauses.emplace_back(
418 makeClause(llvm::omp::Clause::OMPC_lastprivate,
419 LastprivateTy{{/*LastprivateModifier=*/conditional,
420 /*List=*/std::move(lastpcObj)}}));
421 }
422}
423} // namespace tomp
424
425#endif // LLVM_FRONTEND_OPENMP_CONSTRUCTCOMPOSITIONT_H
This file implements the BitVector class.
static CSKYCP::CSKYCPModifier getModifier(unsigned Flags)
Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx
expand reductions
static void r2(uint32_t &A, uint32_t &B, uint32_t &C, uint32_t &D, uint32_t &E, int I, uint32_t *Buf)
Definition: SHA1.cpp:51
static void r1(uint32_t &A, uint32_t &B, uint32_t &C, uint32_t &D, uint32_t &E, int I, uint32_t *Buf)
Definition: SHA1.cpp:45
SI Form memory clauses
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
@ None
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
bool empty() const
Definition: SmallVector.h:95
reference emplace_back(ArgTypes &&... Args)
Definition: SmallVector.h:951
void push_back(const T &Elt)
Definition: SmallVector.h:427
Directive getCompoundConstruct(ArrayRef< Directive > Parts)
Definition: OMP.cpp:129
LLVM_ATTRIBUTE_ALWAYS_INLINE DynamicAPInt mod(const DynamicAPInt &LHS, const DynamicAPInt &RHS)
is always non-negative.
Definition: DynamicAPInt.h:380
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition: STLExtras.h:2406
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
Definition: STLExtras.h:2073
OutputIt transform(R &&Range, OutputIt d_first, UnaryFunction F)
Wrapper function around std::transform to apply a function to a range and store the result elsewhere.
Definition: STLExtras.h:1928
auto find_if(R &&Range, UnaryPredicate P)
Provide wrappers to std::find_if which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1749
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition: STLExtras.h:1879
Definition: ClauseT.h:133
Definition: regex2.h:111
typename ClauseTy::IdTy IdTy
typename ClauseTy::ExprTy ExprTy
typename ClauseTy::TypeTy TypeTy
ConstructCompositionT(uint32_t version, llvm::ArrayRef< DirectiveWithClauses< ClauseTy > > leafs)
DirectiveWithClauses< ClauseTy > merged
llvm::omp::Directive id
Definition: ClauseT.h:1319
tomp::type::ListT< ClauseType > clauses
Definition: ClauseT.h:1320