clang 24.0.0git
CIRGenOpenMPConstructDecomposition.h
Go to the documentation of this file.
1//===--- CIRGenOpenMPConstructDecomposition.h -----------------*- C++ -*-===//
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
9#ifndef LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENOPENMPCONSTRUCTDECOMPOSITION_H
10#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENOPENMPCONSTRUCTDECOMPOSITION_H
11
12#include "clang/AST/Decl.h"
13#include "clang/AST/Expr.h"
17#include "llvm/ADT/ArrayRef.h"
18#include "llvm/ADT/SmallVector.h"
19#include "llvm/Frontend/OpenMP/ClauseT.h"
20#include "llvm/Frontend/OpenMP/ConstructDecompositionT.h"
21#include "llvm/Frontend/OpenMP/OMP.h"
22#include "llvm/Support/Casting.h"
23
24#include <cassert>
25#include <optional>
26#include <utility>
27
29
30// tomp type parameters for Clang: objects are identified by their canonical
31// declaration, expressions and types are the corresponding AST nodes.
32using TypeTy = const clang::Type *;
33using IdTy = const clang::ValueDecl *;
34using ExprTy = const clang::Expr *;
35
36} // namespace clang::CIRGen::omp
37
38// The decomposition operates on tomp::ObjectT<IdTy, ExprTy>; provide the
39// specialization for our identity/expression types.
40namespace tomp::type {
41template <>
42struct ObjectT<clang::CIRGen::omp::IdTy, clang::CIRGen::omp::ExprTy> {
45
46 IdTy id() const { return identity; }
47 const std::optional<ExprTy> &ref() const { return designator; }
48
49 bool operator<(const ObjectT &other) const {
50 return identity < other.identity;
51 }
52
53 IdTy identity = nullptr;
54 std::optional<ExprTy> designator;
55};
56} // namespace tomp::type
57
58namespace clang::CIRGen::omp {
59
60using Object = tomp::ObjectT<IdTy, ExprTy>;
61using ObjectList = tomp::ObjectListT<IdTy, ExprTy>;
62using ClauseBase = tomp::ClauseT<TypeTy, IdTy, ExprTy>;
63
64/// A tomp clause that remembers the Clang AST clause it came from, so the
65/// existing emitters can emit it after the decomposition assigns it to a leaf.
66/// Synthesized clauses have no AST counterpart and leave `original` null.
67struct Clause : public ClauseBase {
68 Clause() = default;
69 Clause(ClauseBase &&base) : ClauseBase(std::move(base)) {}
70 const clang::OMPClause *original = nullptr;
71};
72
74 e = e->IgnoreParenImpCasts();
75 for (;;) {
76 if (const auto *ase = llvm::dyn_cast<clang::ArraySubscriptExpr>(e)) {
77 e = ase->getBase()->IgnoreParenImpCasts();
78 continue;
79 }
80 if (const auto *ase = llvm::dyn_cast<clang::ArraySectionExpr>(e)) {
81 e = ase->getBase()->IgnoreParenImpCasts();
82 continue;
83 }
84 break;
85 }
86 if (const auto *dre = llvm::dyn_cast<clang::DeclRefExpr>(e))
87 return llvm::cast<clang::ValueDecl>(dre->getDecl()->getCanonicalDecl());
88 if (const auto *me = llvm::dyn_cast<clang::MemberExpr>(e))
89 return llvm::cast<clang::ValueDecl>(
90 me->getMemberDecl()->getCanonicalDecl());
91 return nullptr;
92}
93
94/// Build a tomp Object whose identity is the base variable's canonical decl.
95inline Object makeObject(const clang::Expr *e) {
96 return Object{getBaseValueDecl(e), e};
97}
98
99/// Build the tomp object list from a Clang var-list clause.
100template <typename ClangClause>
101inline ObjectList makeObjects(const ClangClause &c) {
102 ObjectList list;
103 for (const clang::Expr *e : c.getVarRefs())
104 list.push_back(makeObject(e));
105 return list;
106}
107
108/// Wrap a tomp clause payload into a Clause, remembering its AST origin.
109template <typename Specific>
110inline Clause makeClause(llvm::omp::Clause id, Specific &&specific,
111 const clang::OMPClause &original) {
112 Clause c{ClauseBase{id, std::forward<Specific>(specific)}};
113 c.original = &original;
114 return c;
115}
116
117/// Clause kinds that need a dedicated conversion: they either have a specific
118/// applyClause() overload (so the payload type selects it) or their contents
119/// feed the algorithm. Guards the generic fallback in makeGeneric.
120inline bool needsSpecificHandling(llvm::omp::Clause kind) {
121 switch (kind) {
122 case llvm::omp::OMPC_allocate:
123 case llvm::omp::OMPC_collapse:
124 case llvm::omp::OMPC_default:
125 case llvm::omp::OMPC_dyn_groupprivate:
126 case llvm::omp::OMPC_firstprivate:
127 case llvm::omp::OMPC_if:
128 case llvm::omp::OMPC_lastprivate:
129 case llvm::omp::OMPC_linear:
130 case llvm::omp::OMPC_map:
131 case llvm::omp::OMPC_nowait:
132 case llvm::omp::OMPC_ompx_attribute:
133 case llvm::omp::OMPC_ompx_bare:
134 case llvm::omp::OMPC_order:
135 case llvm::omp::OMPC_private:
136 case llvm::omp::OMPC_reduction:
137 case llvm::omp::OMPC_shared:
138 case llvm::omp::OMPC_thread_limit:
139 return true;
140 default:
141 return false;
142 }
143}
144
145/// Clause kinds CIR is able to emit today.
146inline bool isEmittableClause(llvm::omp::Clause kind) {
147 switch (kind) {
148 case llvm::omp::OMPC_map:
149 case llvm::omp::OMPC_proc_bind:
150 return true;
151 default:
152 return false;
153 }
154}
155
156/// Represent a clause by kind only, using an inert empty payload that routes
157/// through the algorithm's generic applyClause() path (which reads just the
158/// clause id). Valid for any clause with no specific applyClause() overload.
159inline Clause makeGeneric(llvm::omp::Clause id, const clang::OMPClause &orig) {
160 assert((!isEmittableClause(id) || !needsSpecificHandling(id)) &&
161 "CIR-emittable clause needs specific decomposition handling");
162 return makeClause(id, tomp::clause::ThreadsT<TypeTy, IdTy, ExprTy>{}, orig);
163}
164
165/// Convert a single Clang clause to its tomp representation. Every kind is
166/// handled; contents are populated only where the algorithm reads them.
168 namespace tc = tomp::clause;
169 const llvm::omp::Clause kind = c.getClauseKind();
170 switch (kind) {
171 // Clauses whose contents the algorithm inspects.
172 case llvm::omp::OMPC_map: {
173 tc::MapT<TypeTy, IdTy, ExprTy> m{
174 {/*MapType=*/std::nullopt, /*MapTypeModifiers=*/std::nullopt,
175 /*AttachModifier=*/std::nullopt, /*RefModifier=*/std::nullopt,
176 /*Mappers=*/std::nullopt, /*Iterator=*/std::nullopt,
177 /*LocatorList=*/makeObjects(llvm::cast<clang::OMPMapClause>(c))}};
178 return makeClause(kind, std::move(m), c);
179 }
180 case llvm::omp::OMPC_firstprivate:
181 return makeClause(
182 kind,
183 tc::FirstprivateT<TypeTy, IdTy, ExprTy>{
184 /*List=*/makeObjects(llvm::cast<clang::OMPFirstprivateClause>(c))},
185 c);
186 case llvm::omp::OMPC_private:
187 return makeClause(kind,
188 tc::PrivateT<TypeTy, IdTy, ExprTy>{/*List=*/makeObjects(
189 llvm::cast<clang::OMPPrivateClause>(c))},
190 c);
191 case llvm::omp::OMPC_shared:
192 return makeClause(kind,
193 tc::SharedT<TypeTy, IdTy, ExprTy>{/*List=*/makeObjects(
194 llvm::cast<clang::OMPSharedClause>(c))},
195 c);
196 case llvm::omp::OMPC_lastprivate:
197 return makeClause(
198 kind,
199 tc::LastprivateT<TypeTy, IdTy, ExprTy>{
200 {/*LastprivateModifier=*/std::nullopt,
201 /*List=*/makeObjects(llvm::cast<clang::OMPLastprivateClause>(c))}},
202 c);
203 case llvm::omp::OMPC_linear:
204 return makeClause(
205 kind,
206 tc::LinearT<TypeTy, IdTy, ExprTy>{
207 {/*StepComplexModifier=*/std::nullopt,
208 /*LinearModifier=*/std::nullopt,
209 /*List=*/makeObjects(llvm::cast<clang::OMPLinearClause>(c))}},
210 c);
211 case llvm::omp::OMPC_reduction:
212 return makeClause(
213 kind,
214 tc::ReductionT<TypeTy, IdTy, ExprTy>{
215 {/*ReductionModifier=*/std::nullopt, /*ReductionIdentifiers=*/{},
216 /*List=*/makeObjects(llvm::cast<clang::OMPReductionClause>(c))}},
217 c);
218 case llvm::omp::OMPC_if: {
219 const auto &ic = llvm::cast<clang::OMPIfClause>(c);
220 std::optional<llvm::omp::Directive> mod;
221 if (ic.getNameModifier() != llvm::omp::OMPD_unknown)
222 mod = ic.getNameModifier();
223 return makeClause(
224 kind,
225 tc::IfT<TypeTy, IdTy, ExprTy>{{/*DirectiveNameModifier=*/mod,
226 /*IfExpression=*/ic.getCondition()}},
227 c);
228 }
229 // Clauses with a specific applyClause() overload but no contents the
230 // algorithm reads: carry the correct payload type so dispatch selects it.
231 case llvm::omp::OMPC_allocate:
232 return makeClause(kind,
233 tc::AllocateT<TypeTy, IdTy, ExprTy>{
234 {std::nullopt, std::nullopt, /*List=*/{}}},
235 c);
236 case llvm::omp::OMPC_collapse:
237 return makeClause(kind, tc::CollapseT<TypeTy, IdTy, ExprTy>{/*N=*/nullptr},
238 c);
239 case llvm::omp::OMPC_default:
240 return makeClause(
241 kind,
242 tc::DefaultT<TypeTy, IdTy, ExprTy>{
243 tc::DefaultT<TypeTy, IdTy, ExprTy>::DataSharingAttribute::Shared},
244 c);
245 case llvm::omp::OMPC_dyn_groupprivate:
246 return makeClause(kind,
247 tc::DynGroupprivateT<TypeTy, IdTy, ExprTy>{
248 {std::nullopt, std::nullopt, /*Size=*/nullptr}},
249 c);
250 case llvm::omp::OMPC_nowait:
251 return makeClause(kind, tc::NowaitT<TypeTy, IdTy, ExprTy>{}, c);
252 case llvm::omp::OMPC_ompx_attribute:
253 return makeClause(kind, tc::OmpxAttributeT<TypeTy, IdTy, ExprTy>{}, c);
254 case llvm::omp::OMPC_ompx_bare:
255 return makeClause(kind, tc::OmpxBareT<TypeTy, IdTy, ExprTy>{}, c);
256 case llvm::omp::OMPC_order:
257 return makeClause(
258 kind,
259 tc::OrderT<TypeTy, IdTy, ExprTy>{
260 {std::nullopt,
261 tc::OrderT<TypeTy, IdTy, ExprTy>::Ordering::Concurrent}},
262 c);
263 case llvm::omp::OMPC_thread_limit:
264 return makeClause(kind, tc::ThreadLimitT<TypeTy, IdTy, ExprTy>{/*List=*/{}},
265 c);
266 // Everything else routes by kind alone.
267 default:
268 return makeGeneric(kind, c);
269 }
270}
271
272/// Helper required by ConstructDecompositionT.
274 /// Our object identities are already normalized to the base variable's decl,
275 /// so an object is its own base.
276 std::optional<Object> getBaseObject(const Object &object) const {
277 return object;
278 }
279 /// CIR does not lower loop directives yet, so there is no iteration variable.
280 std::optional<Object> getLoopIterVar() const { return std::nullopt; }
281 /// Defer to the spec table; CIR does not override allowed clauses per
282 /// directive.
283 bool isClauseAllowedOnDirective(llvm::omp::Clause clauseId,
284 llvm::omp::Directive dirId,
285 llvm::omp::Version version) const {
286 return llvm::omp::isAllowedClauseForDirective(dirId, clauseId, version);
287 }
288};
289
295
297
298/// The decomposition algorithm resolves a directive-name-modifier on an `if`
299/// clause (e.g. `if(parallel: cond)`) by synthesizing a fresh clause with the
300/// modifier stripped -- reusing the same condition expression -- and
301/// assigning it to the leaf named by the modifier. That synthesized clause
302/// has no Clang AST node of its own (\c original is null), but it shares its
303/// condition expression's identity with the clause it was derived from. Use
304/// that to recover the original \c OMPIfClause so a resolved `if` clause is
305/// routed to its leaf like any other clause, instead of being reported as an
306/// unimplemented synthesized clause.
307inline const clang::OMPClause *
309 const auto &ifClause = std::get<tomp::clause::IfT<TypeTy, IdTy, ExprTy>>(c.u);
310 const clang::Expr *condition = std::get<ExprTy>(ifClause.t);
311 for (const clang::OMPClause *oc : s.clauses()) {
312 const auto *ic = llvm::dyn_cast<clang::OMPIfClause>(oc);
313 if (ic && ic->getCondition() == condition)
314 return ic;
315 }
316 return nullptr;
317}
318
319/// Given a potentially compound directive with a list of clauses that apply to
320/// it, break it up into individual leaf constructs each with the subset of
321/// applicable clauses (plus implicit clauses, if any). From that create a work
322/// queue, ordered outermost to innermost, where each work item corresponds to
323/// the leaf construct with its clauses. Implicit clauses are synthesized by the
324/// decomposition and have no Clang AST node, so they are listed separately.
325inline ConstructQueue buildConstructQueue(unsigned openmpVersion,
326 const OMPExecutableDirective &s) {
328 for (const OMPClause *c : s.clauses())
329 input.push_back(convertClause(*c));
330
331 DecompositionHelper helper;
332 tomp::ConstructDecompositionT<Clause, DecompositionHelper> decomp(
333 llvm::omp::Version(openmpVersion), helper, s.getDirectiveKind(),
335
336 ConstructQueue result;
337 for (const tomp::DirectiveWithClauses<Clause> &dwc : decomp.output) {
338 LeafWithClauses leaf;
339 leaf.id = dwc.id;
340 for (const Clause &c : dwc.clauses) {
341 if (c.original) {
342 leaf.clauses.push_back(c.original);
343 continue;
344 }
345 if (c.id == llvm::omp::OMPC_if) {
346 if (const clang::OMPClause *orig = findOriginalIfClause(s, c)) {
347 leaf.clauses.push_back(orig);
348 continue;
349 }
350 }
351 leaf.synthesized.push_back(c.id);
352 }
353 result.push_back(std::move(leaf));
354 }
355 return result;
356}
357
358inline bool isLastItemInQueue(ConstructQueue::const_iterator item,
359 const ConstructQueue &queue) {
360 return std::next(item) == queue.end();
361}
362
363} // namespace clang::CIRGen::omp
364
365#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENOPENMPCONSTRUCTDECOMPOSITION_H
This file defines OpenMP AST classes for clauses.
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a an optional score condition
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
Defines some OpenMP-specific enums and functions.
This file defines OpenMP AST classes for executable directives and clauses.
This represents one expression.
Definition Expr.h:113
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3123
This is a basic class for representing single OpenMP clause.
OpenMPClauseKind getClauseKind() const
Returns kind of OpenMP clause (private, shared, reduction, etc.).
The base class of the type hierarchy.
Definition TypeBase.h:1879
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:713
tomp::ObjectListT< IdTy, ExprTy > ObjectList
bool isLastItemInQueue(ConstructQueue::const_iterator item, const ConstructQueue &queue)
llvm::SmallVector< LeafWithClauses > ConstructQueue
const clang::OMPClause * findOriginalIfClause(const OMPExecutableDirective &s, const Clause &c)
The decomposition algorithm resolves a directive-name-modifier on an if clause (e....
Clause makeClause(llvm::omp::Clause id, Specific &&specific, const clang::OMPClause &original)
Wrap a tomp clause payload into a Clause, remembering its AST origin.
Object makeObject(const clang::Expr *e)
Build a tomp Object whose identity is the base variable's canonical decl.
const clang::ValueDecl * getBaseValueDecl(const clang::Expr *e)
tomp::ClauseT< TypeTy, IdTy, ExprTy > ClauseBase
bool isEmittableClause(llvm::omp::Clause kind)
Clause kinds CIR is able to emit today.
ObjectList makeObjects(const ClangClause &c)
Build the tomp object list from a Clang var-list clause.
bool needsSpecificHandling(llvm::omp::Clause kind)
Clause kinds that need a dedicated conversion: they either have a specific applyClause() overload (so...
Clause makeGeneric(llvm::omp::Clause id, const clang::OMPClause &orig)
Represent a clause by kind only, using an inert empty payload that routes through the algorithm's gen...
tomp::ObjectT< IdTy, ExprTy > Object
ConstructQueue buildConstructQueue(unsigned openmpVersion, const OMPExecutableDirective &s)
Given a potentially compound directive with a list of clauses that apply to it, break it up into indi...
Clause convertClause(const clang::OMPClause &c)
Convert a single Clang clause to its tomp representation.
Top level wrappers for InstallAPI frontend operations.
A tomp clause that remembers the Clang AST clause it came from, so the existing emitters can emit it ...
Helper required by ConstructDecompositionT.
std::optional< Object > getBaseObject(const Object &object) const
Our object identities are already normalized to the base variable's decl, so an object is its own bas...
bool isClauseAllowedOnDirective(llvm::omp::Clause clauseId, llvm::omp::Directive dirId, llvm::omp::Version version) const
Defer to the spec table; CIR does not override allowed clauses per directive.
std::optional< Object > getLoopIterVar() const
CIR does not lower loop directives yet, so there is no iteration variable.
llvm::SmallVector< llvm::omp::Clause > synthesized
llvm::SmallVector< const clang::OMPClause * > clauses