1//===----------------------------------------------------------------------===//
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// Emit OpenACC clause nodes as CIR code.
10//
11//===----------------------------------------------------------------------===//
12
13#include <type_traits>
14
15#include "CIRGenFunction.h"
16
17#include "clang/AST/ExprCXX.h"
18
19#include "mlir/Dialect/Arith/IR/Arith.h"
20#include "mlir/Dialect/OpenACC/OpenACC.h"
21#include "llvm/ADT/TypeSwitch.h"
22
23using namespace clang;
24using namespace clang::CIRGen;
25
26namespace {
27// Simple type-trait to see if the first template arg is one of the list, so we
28// can tell whether to `if-constexpr` a bunch of stuff.
29template <typename ToTest, typename T, typename... Tys>
30constexpr bool isOneOfTypes =
31 std::is_same_v<ToTest, T> || isOneOfTypes<ToTest, Tys...>;
32template <typename ToTest, typename T>
33constexpr bool isOneOfTypes<ToTest, T> = std::is_same_v<ToTest, T>;
34
35// Holds information for emitting clauses for a combined construct. We
36// instantiate the clause emitter with this type so that it can use
37// if-constexpr to specially handle these.
38template <typename CompOpTy> struct CombinedConstructClauseInfo {
39 using ComputeOpTy = CompOpTy;
40 ComputeOpTy computeOp;
41 mlir::acc::LoopOp loopOp;
42};
43template <typename ToTest> constexpr bool isCombinedType = false;
44template <typename T>
45constexpr bool isCombinedType<CombinedConstructClauseInfo<T>> = true;
46
47template <typename OpTy>
48class OpenACCClauseCIREmitter final
49 : public OpenACCClauseVisitor<OpenACCClauseCIREmitter<OpTy>> {
50 // Necessary for combined constructs.
51 template <typename FriendOpTy> friend class OpenACCClauseCIREmitter;
52
53 OpTy &operation;
54 CIRGen::CIRGenFunction &cgf;
55 CIRGen::CIRGenBuilderTy &builder;
56
57 // This is necessary since a few of the clauses emit differently based on the
58 // directive kind they are attached to.
59 OpenACCDirectiveKind dirKind;
60 // TODO(cir): This source location should be able to go away once the NYI
61 // diagnostics are gone.
62 SourceLocation dirLoc;
63
64 llvm::SmallVector<mlir::acc::DeviceType> lastDeviceTypeValues;
65 // Keep track of the async-clause so that we can shortcut updating the data
66 // operands async clauses.
67 bool hasAsyncClause = false;
68 // Keep track of the data operands so that we can update their async clauses.
69 llvm::SmallVector<mlir::Operation *> dataOperands;
70
71 void clauseNotImplemented(const OpenACCClause &c) {
72 cgf.cgm.errorNYI(loc: c.getSourceRange(), feature: "OpenACC Clause", name: c.getClauseKind());
73 }
74
75 void setLastDeviceTypeClause(const OpenACCDeviceTypeClause &clause) {
76 lastDeviceTypeValues.clear();
77
78 llvm::for_each(clause.getArchitectures(),
79 [this](const DeviceTypeArgument &arg) {
80 lastDeviceTypeValues.push_back(
81 decodeDeviceType(arg.getIdentifierInfo()));
82 });
83 }
84
85 mlir::Value emitIntExpr(const Expr *intExpr) {
86 mlir::Value expr = cgf.emitScalarExpr(intExpr);
87 mlir::Location exprLoc = cgf.cgm.getLoc(intExpr->getBeginLoc());
88
89 mlir::IntegerType targetType = mlir::IntegerType::get(
90 &cgf.getMLIRContext(), cgf.getContext().getIntWidth(intExpr->getType()),
91 intExpr->getType()->isSignedIntegerOrEnumerationType()
92 ? mlir::IntegerType::SignednessSemantics::Signed
93 : mlir::IntegerType::SignednessSemantics::Unsigned);
94
95 auto conversionOp = builder.create<mlir::UnrealizedConversionCastOp>(
96 exprLoc, targetType, expr);
97 return conversionOp.getResult(0);
98 }
99
100 // 'condition' as an OpenACC grammar production is used for 'if' and (some
101 // variants of) 'self'. It needs to be emitted as a signless-1-bit value, so
102 // this function emits the expression, then sets the unrealized conversion
103 // cast correctly, and returns the completed value.
104 mlir::Value createCondition(const Expr *condExpr) {
105 mlir::Value condition = cgf.evaluateExprAsBool(condExpr);
106 mlir::Location exprLoc = cgf.cgm.getLoc(condExpr->getBeginLoc());
107 mlir::IntegerType targetType = mlir::IntegerType::get(
108 &cgf.getMLIRContext(), /*width=*/1,
109 mlir::IntegerType::SignednessSemantics::Signless);
110 auto conversionOp = builder.create<mlir::UnrealizedConversionCastOp>(
111 exprLoc, targetType, condition);
112 return conversionOp.getResult(0);
113 }
114
115 mlir::Value createConstantInt(mlir::Location loc, unsigned width,
116 int64_t value) {
117 mlir::IntegerType ty = mlir::IntegerType::get(
118 &cgf.getMLIRContext(), width,
119 mlir::IntegerType::SignednessSemantics::Signless);
120 auto constOp = builder.create<mlir::arith::ConstantOp>(
121 loc, builder.getIntegerAttr(ty, value));
122
123 return constOp.getResult();
124 }
125
126 mlir::Value createConstantInt(SourceLocation loc, unsigned width,
127 int64_t value) {
128 return createConstantInt(cgf.cgm.getLoc(loc), width, value);
129 }
130
131 mlir::acc::DeviceType decodeDeviceType(const IdentifierInfo *ii) {
132 // '*' case leaves no identifier-info, just a nullptr.
133 if (!ii)
134 return mlir::acc::DeviceType::Star;
135 return llvm::StringSwitch<mlir::acc::DeviceType>(ii->getName())
136 .CaseLower("default", mlir::acc::DeviceType::Default)
137 .CaseLower("host", mlir::acc::DeviceType::Host)
138 .CaseLower("multicore", mlir::acc::DeviceType::Multicore)
139 .CasesLower("nvidia", "acc_device_nvidia",
140 mlir::acc::DeviceType::Nvidia)
141 .CaseLower("radeon", mlir::acc::DeviceType::Radeon);
142 }
143
144 mlir::acc::GangArgType decodeGangType(OpenACCGangKind gk) {
145 switch (gk) {
146 case OpenACCGangKind::Num:
147 return mlir::acc::GangArgType::Num;
148 case OpenACCGangKind::Dim:
149 return mlir::acc::GangArgType::Dim;
150 case OpenACCGangKind::Static:
151 return mlir::acc::GangArgType::Static;
152 }
153 llvm_unreachable("unknown gang kind");
154 }
155
156 template <typename U = void,
157 typename = std::enable_if_t<isCombinedType<OpTy>, U>>
158 void applyToLoopOp(const OpenACCClause &c) {
159 mlir::OpBuilder::InsertionGuard guardCase(builder);
160 builder.setInsertionPoint(operation.loopOp);
161 OpenACCClauseCIREmitter<mlir::acc::LoopOp> loopEmitter{
162 operation.loopOp, cgf, builder, dirKind, dirLoc};
163 loopEmitter.lastDeviceTypeValues = lastDeviceTypeValues;
164 loopEmitter.Visit(&c);
165 }
166
167 template <typename U = void,
168 typename = std::enable_if_t<isCombinedType<OpTy>, U>>
169 void applyToComputeOp(const OpenACCClause &c) {
170 mlir::OpBuilder::InsertionGuard guardCase(builder);
171 builder.setInsertionPoint(operation.computeOp);
172 OpenACCClauseCIREmitter<typename OpTy::ComputeOpTy> computeEmitter{
173 operation.computeOp, cgf, builder, dirKind, dirLoc};
174
175 computeEmitter.lastDeviceTypeValues = lastDeviceTypeValues;
176
177 // Async handler uses the first data operand to figure out where to insert
178 // its information if it is present. This ensures that the new handler will
179 // correctly set the insertion point for async.
180 if (!dataOperands.empty())
181 computeEmitter.dataOperands.push_back(dataOperands.front());
182 computeEmitter.Visit(&c);
183
184 // Make sure all of the new data operands are kept track of here. The
185 // combined constructs always apply 'async' to only the compute component,
186 // so we need to collect these.
187 dataOperands.append(computeEmitter.dataOperands);
188 }
189
190 struct DataOperandInfo {
191 mlir::Location beginLoc;
192 mlir::Value varValue;
193 std::string name;
194 llvm::SmallVector<mlir::Value> bounds;
195 };
196
197 mlir::Value createBound(mlir::Location boundLoc, mlir::Value lowerBound,
198 mlir::Value upperBound, mlir::Value extent) {
199 // Arrays always have a start-idx of 0.
200 mlir::Value startIdx = createConstantInt(boundLoc, 64, 0);
201 // Stride is always 1 in C/C++.
202 mlir::Value stride = createConstantInt(boundLoc, 64, 1);
203
204 auto bound = builder.create<mlir::acc::DataBoundsOp>(boundLoc, lowerBound,
205 upperBound);
206 bound.getStartIdxMutable().assign(startIdx);
207 if (extent)
208 bound.getExtentMutable().assign(extent);
209 bound.getStrideMutable().assign(stride);
210
211 return bound;
212 }
213
214 // A helper function that gets the information from an operand to a data
215 // clause, so that it can be used to emit the data operations.
216 DataOperandInfo getDataOperandInfo(OpenACCDirectiveKind dk, const Expr *e) {
217 // TODO: OpenACC: Cache was different enough as to need a separate
218 // `ActOnCacheVar`, so we are going to need to do some investigations here
219 // when it comes to implement this for cache.
220 if (dk == OpenACCDirectiveKind::Cache) {
221 cgf.cgm.errorNYI(e->getSourceRange(),
222 "OpenACC data operand for 'cache' directive");
223 return {cgf.cgm.getLoc(e->getBeginLoc()), {}, {}, {}};
224 }
225
226 const Expr *curVarExpr = e->IgnoreParenImpCasts();
227
228 mlir::Location exprLoc = cgf.cgm.getLoc(curVarExpr->getBeginLoc());
229 llvm::SmallVector<mlir::Value> bounds;
230
231 std::string exprString;
232 llvm::raw_string_ostream os(exprString);
233 e->printPretty(os, nullptr, cgf.getContext().getPrintingPolicy());
234
235 // Assemble the list of bounds.
236 while (isa<ArraySectionExpr, ArraySubscriptExpr>(Val: curVarExpr)) {
237 mlir::Location boundLoc = cgf.cgm.getLoc(curVarExpr->getBeginLoc());
238 mlir::Value lowerBound;
239 mlir::Value upperBound;
240 mlir::Value extent;
241
242 if (const auto *section = dyn_cast<ArraySectionExpr>(Val: curVarExpr)) {
243 if (const Expr *lb = section->getLowerBound())
244 lowerBound = emitIntExpr(lb);
245 else
246 lowerBound = createConstantInt(boundLoc, 64, 0);
247
248 if (const Expr *len = section->getLength()) {
249 extent = emitIntExpr(len);
250 } else {
251 QualType baseTy = ArraySectionExpr::getBaseOriginalType(
252 Base: section->getBase()->IgnoreParenImpCasts());
253 // We know this is the case as implicit lengths are only allowed for
254 // array types with a constant size, or a dependent size. AND since
255 // we are codegen we know we're not dependent.
256 auto *arrayTy = cgf.getContext().getAsConstantArrayType(T: baseTy);
257 // Rather than trying to calculate the extent based on the
258 // lower-bound, we can just emit this as an upper bound.
259 upperBound =
260 createConstantInt(boundLoc, 64, arrayTy->getLimitedSize() - 1);
261 }
262
263 curVarExpr = section->getBase()->IgnoreParenImpCasts();
264 } else {
265 const auto *subscript = cast<ArraySubscriptExpr>(Val: curVarExpr);
266
267 lowerBound = emitIntExpr(subscript->getIdx());
268 // Length of an array index is always 1.
269 extent = createConstantInt(boundLoc, 64, 1);
270 curVarExpr = subscript->getBase()->IgnoreParenImpCasts();
271 }
272
273 bounds.push_back(createBound(boundLoc, lowerBound, upperBound, extent));
274 }
275
276 if (const auto *memExpr = dyn_cast<MemberExpr>(Val: curVarExpr))
277 return {exprLoc, cgf.emitMemberExpr(e: memExpr).getPointer(), exprString,
278 std::move(bounds)};
279
280 // Sema has made sure that only 4 types of things can get here, array
281 // subscript, array section, member expr, or DRE to a var decl (or the
282 // former 3 wrapping a var-decl), so we should be able to assume this is
283 // right.
284 const auto *dre = cast<DeclRefExpr>(Val: curVarExpr);
285 return {exprLoc, cgf.emitDeclRefLValue(e: dre).getPointer(), exprString,
286 std::move(bounds)};
287 }
288
289 template <typename BeforeOpTy, typename AfterOpTy>
290 void addDataOperand(const Expr *varOperand, mlir::acc::DataClause dataClause,
291 bool structured, bool implicit) {
292 DataOperandInfo opInfo = getDataOperandInfo(dk: dirKind, e: varOperand);
293
294 // TODO: OpenACC: we should comprehend the 'modifier-list' here for the data
295 // operand. At the moment, we don't have a uniform way to assign these
296 // properly, and the dialect cannot represent anything other than 'readonly'
297 // and 'zero' on copyin/copyout/create, so for now, we skip it.
298
299 auto beforeOp =
300 builder.create<BeforeOpTy>(opInfo.beginLoc, opInfo.varValue, structured,
301 implicit, opInfo.name, opInfo.bounds);
302 operation.getDataClauseOperandsMutable().append(beforeOp.getResult());
303
304 AfterOpTy afterOp;
305 {
306 mlir::OpBuilder::InsertionGuard guardCase(builder);
307 builder.setInsertionPointAfter(operation);
308
309 if constexpr (std::is_same_v<AfterOpTy, mlir::acc::DeleteOp> ||
310 std::is_same_v<AfterOpTy, mlir::acc::DetachOp>) {
311 // Detach/Delete ops don't have the variable reference here, so they
312 // take 1 fewer argument to their build function.
313 afterOp = builder.create<AfterOpTy>(
314 opInfo.beginLoc, beforeOp.getResult(), structured, implicit,
315 opInfo.name, opInfo.bounds);
316 } else {
317 afterOp = builder.create<AfterOpTy>(
318 opInfo.beginLoc, beforeOp.getResult(), opInfo.varValue, structured,
319 implicit, opInfo.name, opInfo.bounds);
320 }
321 }
322
323 // Set the 'rest' of the info for both operations.
324 beforeOp.setDataClause(dataClause);
325 afterOp.setDataClause(dataClause);
326
327 // Make sure we record these, so 'async' values can be updated later.
328 dataOperands.push_back(beforeOp.getOperation());
329 dataOperands.push_back(afterOp.getOperation());
330 }
331
332 template <typename BeforeOpTy>
333 void addDataOperand(const Expr *varOperand, mlir::acc::DataClause dataClause,
334 bool structured, bool implicit) {
335 DataOperandInfo opInfo = getDataOperandInfo(dk: dirKind, e: varOperand);
336 auto beforeOp =
337 builder.create<BeforeOpTy>(opInfo.beginLoc, opInfo.varValue, structured,
338 implicit, opInfo.name, opInfo.bounds);
339 operation.getDataClauseOperandsMutable().append(beforeOp.getResult());
340
341 // Set the 'rest' of the info for the operation.
342 beforeOp.setDataClause(dataClause);
343 // Make sure we record these, so 'async' values can be updated later.
344 dataOperands.push_back(beforeOp.getOperation());
345 }
346
347 // Helper function that covers for the fact that we don't have this function
348 // on all operation types.
349 mlir::ArrayAttr getAsyncOnlyAttr() {
350 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
351 mlir::acc::KernelsOp, mlir::acc::DataOp>)
352 return operation.getAsyncOnlyAttr();
353 else if constexpr (isCombinedType<OpTy>)
354 return operation.computeOp.getAsyncOnlyAttr();
355
356 // Note: 'wait' has async as well, but it cannot have data clauses, so we
357 // don't have to handle them here.
358
359 llvm_unreachable("getting asyncOnly when clause not valid on operation?");
360 }
361
362 // Helper function that covers for the fact that we don't have this function
363 // on all operation types.
364 mlir::ArrayAttr getAsyncOperandsDeviceTypeAttr() {
365 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
366 mlir::acc::KernelsOp, mlir::acc::DataOp>)
367 return operation.getAsyncOperandsDeviceTypeAttr();
368 else if constexpr (isCombinedType<OpTy>)
369 return operation.computeOp.getAsyncOperandsDeviceTypeAttr();
370
371 // Note: 'wait' has async as well, but it cannot have data clauses, so we
372 // don't have to handle them here.
373
374 llvm_unreachable(
375 "getting asyncOperandsDeviceType when clause not valid on operation?");
376 }
377
378 // Helper function that covers for the fact that we don't have this function
379 // on all operation types.
380 mlir::OperandRange getAsyncOperands() {
381 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
382 mlir::acc::KernelsOp, mlir::acc::DataOp>)
383 return operation.getAsyncOperands();
384 else if constexpr (isCombinedType<OpTy>)
385 return operation.computeOp.getAsyncOperands();
386
387 // Note: 'wait' has async as well, but it cannot have data clauses, so we
388 // don't have to handle them here.
389
390 llvm_unreachable(
391 "getting asyncOperandsDeviceType when clause not valid on operation?");
392 }
393
394 // The 'data' clauses all require that we add the 'async' values from the
395 // operation to them. We've collected the data operands along the way, so use
396 // that list to get the current 'async' values.
397 void updateDataOperandAsyncValues() {
398 if (!hasAsyncClause || dataOperands.empty())
399 return;
400
401 for (mlir::Operation *dataOp : dataOperands) {
402 llvm::TypeSwitch<mlir::Operation *, void>(dataOp)
403 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto op) {
404 op.setAsyncOnlyAttr(getAsyncOnlyAttr());
405 op.setAsyncOperandsDeviceTypeAttr(getAsyncOperandsDeviceTypeAttr());
406 op.getAsyncOperandsMutable().assign(getAsyncOperands());
407 })
408 .Default([&](mlir::Operation *) {
409 llvm_unreachable("Not a data operation?");
410 });
411 }
412 }
413
414public:
415 OpenACCClauseCIREmitter(OpTy &operation, CIRGen::CIRGenFunction &cgf,
416 CIRGen::CIRGenBuilderTy &builder,
417 OpenACCDirectiveKind dirKind, SourceLocation dirLoc)
418 : operation(operation), cgf(cgf), builder(builder), dirKind(dirKind),
419 dirLoc(dirLoc) {}
420
421 void VisitClause(const OpenACCClause &clause) {
422 clauseNotImplemented(c: clause);
423 }
424
425 // The entry point for the CIR emitter. All users should use this rather than
426 // 'visitClauseList', as this also handles the things that have to happen
427 // 'after' the clauses are all visited.
428 void emitClauses(ArrayRef<const OpenACCClause *> clauses) {
429 this->VisitClauseList(clauses);
430 updateDataOperandAsyncValues();
431 }
432
433 void VisitDefaultClause(const OpenACCDefaultClause &clause) {
434 // This type-trait checks if 'op'(the first arg) is one of the mlir::acc
435 // operations listed in the rest of the arguments.
436 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
437 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
438 switch (clause.getDefaultClauseKind()) {
439 case OpenACCDefaultClauseKind::None:
440 operation.setDefaultAttr(mlir::acc::ClauseDefaultValue::None);
441 break;
442 case OpenACCDefaultClauseKind::Present:
443 operation.setDefaultAttr(mlir::acc::ClauseDefaultValue::Present);
444 break;
445 case OpenACCDefaultClauseKind::Invalid:
446 break;
447 }
448 } else if constexpr (isCombinedType<OpTy>) {
449 applyToComputeOp(clause);
450 } else {
451 llvm_unreachable("Unknown construct kind in VisitDefaultClause");
452 }
453 }
454
455 void VisitDeviceTypeClause(const OpenACCDeviceTypeClause &clause) {
456 setLastDeviceTypeClause(clause);
457
458 if constexpr (isOneOfTypes<OpTy, mlir::acc::InitOp,
459 mlir::acc::ShutdownOp>) {
460 llvm::for_each(
461 clause.getArchitectures(), [this](const DeviceTypeArgument &arg) {
462 operation.addDeviceType(builder.getContext(),
463 decodeDeviceType(arg.getIdentifierInfo()));
464 });
465 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::SetOp>) {
466 assert(!operation.getDeviceTypeAttr() && "already have device-type?");
467 assert(clause.getArchitectures().size() <= 1);
468
469 if (!clause.getArchitectures().empty())
470 operation.setDeviceType(
471 decodeDeviceType(clause.getArchitectures()[0].getIdentifierInfo()));
472 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
473 mlir::acc::SerialOp, mlir::acc::KernelsOp,
474 mlir::acc::DataOp, mlir::acc::LoopOp>) {
475 // Nothing to do here, these constructs don't have any IR for these, as
476 // they just modify the other clauses IR. So setting of
477 // `lastDeviceTypeValues` (done above) is all we need.
478 } else if constexpr (isCombinedType<OpTy>) {
479 // Nothing to do here either, combined constructs are just going to use
480 // 'lastDeviceTypeValues' to set the value for the child visitor.
481 } else {
482 // TODO: When we've implemented this for everything, switch this to an
483 // unreachable. update, data, routine constructs remain.
484 return clauseNotImplemented(c: clause);
485 }
486 }
487
488 void VisitNumWorkersClause(const OpenACCNumWorkersClause &clause) {
489 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
490 mlir::acc::KernelsOp>) {
491 operation.addNumWorkersOperand(builder.getContext(),
492 emitIntExpr(clause.getIntExpr()),
493 lastDeviceTypeValues);
494 } else if constexpr (isCombinedType<OpTy>) {
495 applyToComputeOp(clause);
496 } else {
497 llvm_unreachable("Unknown construct kind in VisitNumGangsClause");
498 }
499 }
500
501 void VisitVectorLengthClause(const OpenACCVectorLengthClause &clause) {
502 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
503 mlir::acc::KernelsOp>) {
504 operation.addVectorLengthOperand(builder.getContext(),
505 emitIntExpr(clause.getIntExpr()),
506 lastDeviceTypeValues);
507 } else if constexpr (isCombinedType<OpTy>) {
508 applyToComputeOp(clause);
509 } else {
510 llvm_unreachable("Unknown construct kind in VisitVectorLengthClause");
511 }
512 }
513
514 void VisitAsyncClause(const OpenACCAsyncClause &clause) {
515 hasAsyncClause = true;
516 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
517 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
518 if (!clause.hasIntExpr())
519 operation.addAsyncOnly(builder.getContext(), lastDeviceTypeValues);
520 else {
521
522 mlir::Value intExpr;
523 {
524 // Async int exprs can be referenced by the data operands, which means
525 // that the int-exprs have to appear before them. IF there is a data
526 // operand already, set the insertion point to 'before' it.
527 mlir::OpBuilder::InsertionGuard guardCase(builder);
528 if (!dataOperands.empty())
529 builder.setInsertionPoint(dataOperands.front());
530 intExpr = emitIntExpr(clause.getIntExpr());
531 }
532 operation.addAsyncOperand(builder.getContext(), intExpr,
533 lastDeviceTypeValues);
534 }
535 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::WaitOp>) {
536 // Wait doesn't have a device_type, so its handling here is slightly
537 // different.
538 if (!clause.hasIntExpr())
539 operation.setAsync(true);
540 else
541 operation.getAsyncOperandMutable().append(
542 emitIntExpr(clause.getIntExpr()));
543 } else if constexpr (isCombinedType<OpTy>) {
544 applyToComputeOp(clause);
545 } else {
546 // TODO: When we've implemented this for everything, switch this to an
547 // unreachable. Combined constructs remain. Data, enter data, exit data,
548 // update constructs remain.
549 return clauseNotImplemented(c: clause);
550 }
551 }
552
553 void VisitSelfClause(const OpenACCSelfClause &clause) {
554 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
555 mlir::acc::KernelsOp>) {
556 if (clause.isEmptySelfClause()) {
557 operation.setSelfAttr(true);
558 } else if (clause.isConditionExprClause()) {
559 assert(clause.hasConditionExpr());
560 operation.getSelfCondMutable().append(
561 createCondition(clause.getConditionExpr()));
562 } else {
563 llvm_unreachable("var-list version of self shouldn't get here");
564 }
565 } else if constexpr (isCombinedType<OpTy>) {
566 applyToComputeOp(clause);
567 } else {
568 // TODO: When we've implemented this for everything, switch this to an
569 // unreachable. update construct remains.
570 return clauseNotImplemented(c: clause);
571 }
572 }
573
574 void VisitIfClause(const OpenACCIfClause &clause) {
575 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
576 mlir::acc::KernelsOp, mlir::acc::InitOp,
577 mlir::acc::ShutdownOp, mlir::acc::SetOp,
578 mlir::acc::DataOp, mlir::acc::WaitOp,
579 mlir::acc::HostDataOp>) {
580 operation.getIfCondMutable().append(
581 createCondition(clause.getConditionExpr()));
582 } else if constexpr (isCombinedType<OpTy>) {
583 applyToComputeOp(clause);
584 } else {
585 // 'if' applies to most of the constructs, but hold off on lowering them
586 // until we can write tests/know what we're doing with codegen to make
587 // sure we get it right.
588 // TODO: When we've implemented this for everything, switch this to an
589 // unreachable. Enter data, exit data, host_data, update constructs
590 // remain.
591 return clauseNotImplemented(c: clause);
592 }
593 }
594
595 void VisitIfPresentClause(const OpenACCIfPresentClause &clause) {
596 if constexpr (isOneOfTypes<OpTy, mlir::acc::HostDataOp>) {
597 operation.setIfPresent(true);
598 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::UpdateOp>) {
599 // Last unimplemented one here, so just put it in this way instead.
600 return clauseNotImplemented(c: clause);
601 } else {
602 llvm_unreachable("unknown construct kind in VisitIfPresentClause");
603 }
604 }
605
606 void VisitDeviceNumClause(const OpenACCDeviceNumClause &clause) {
607 if constexpr (isOneOfTypes<OpTy, mlir::acc::InitOp, mlir::acc::ShutdownOp,
608 mlir::acc::SetOp>) {
609 operation.getDeviceNumMutable().append(emitIntExpr(clause.getIntExpr()));
610 } else {
611 llvm_unreachable(
612 "init, shutdown, set, are only valid device_num constructs");
613 }
614 }
615
616 void VisitNumGangsClause(const OpenACCNumGangsClause &clause) {
617 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
618 mlir::acc::KernelsOp>) {
619 llvm::SmallVector<mlir::Value> values;
620 for (const Expr *E : clause.getIntExprs())
621 values.push_back(emitIntExpr(E));
622
623 operation.addNumGangsOperands(builder.getContext(), values,
624 lastDeviceTypeValues);
625 } else if constexpr (isCombinedType<OpTy>) {
626 applyToComputeOp(clause);
627 } else {
628 llvm_unreachable("Unknown construct kind in VisitNumGangsClause");
629 }
630 }
631
632 void VisitWaitClause(const OpenACCWaitClause &clause) {
633 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
634 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
635 if (!clause.hasExprs()) {
636 operation.addWaitOnly(builder.getContext(), lastDeviceTypeValues);
637 } else {
638 llvm::SmallVector<mlir::Value> values;
639 if (clause.hasDevNumExpr())
640 values.push_back(emitIntExpr(clause.getDevNumExpr()));
641 for (const Expr *E : clause.getQueueIdExprs())
642 values.push_back(emitIntExpr(E));
643 operation.addWaitOperands(builder.getContext(), clause.hasDevNumExpr(),
644 values, lastDeviceTypeValues);
645 }
646 } else if constexpr (isCombinedType<OpTy>) {
647 applyToComputeOp(clause);
648 } else {
649 // TODO: When we've implemented this for everything, switch this to an
650 // unreachable. Enter data, exit data, update constructs remain.
651 return clauseNotImplemented(c: clause);
652 }
653 }
654
655 void VisitDefaultAsyncClause(const OpenACCDefaultAsyncClause &clause) {
656 if constexpr (isOneOfTypes<OpTy, mlir::acc::SetOp>) {
657 operation.getDefaultAsyncMutable().append(
658 emitIntExpr(clause.getIntExpr()));
659 } else {
660 llvm_unreachable("set, is only valid device_num constructs");
661 }
662 }
663
664 void VisitSeqClause(const OpenACCSeqClause &clause) {
665 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
666 operation.addSeq(builder.getContext(), lastDeviceTypeValues);
667 } else if constexpr (isCombinedType<OpTy>) {
668 applyToLoopOp(clause);
669 } else {
670 // TODO: When we've implemented this for everything, switch this to an
671 // unreachable. Routine construct remains.
672 return clauseNotImplemented(c: clause);
673 }
674 }
675
676 void VisitAutoClause(const OpenACCAutoClause &clause) {
677 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
678 operation.addAuto(builder.getContext(), lastDeviceTypeValues);
679 } else if constexpr (isCombinedType<OpTy>) {
680 applyToLoopOp(clause);
681 } else {
682 // TODO: When we've implemented this for everything, switch this to an
683 // unreachable. Routine, construct remains.
684 return clauseNotImplemented(c: clause);
685 }
686 }
687
688 void VisitIndependentClause(const OpenACCIndependentClause &clause) {
689 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
690 operation.addIndependent(builder.getContext(), lastDeviceTypeValues);
691 } else if constexpr (isCombinedType<OpTy>) {
692 applyToLoopOp(clause);
693 } else {
694 // TODO: When we've implemented this for everything, switch this to an
695 // unreachable. Routine construct remains.
696 return clauseNotImplemented(c: clause);
697 }
698 }
699
700 void VisitCollapseClause(const OpenACCCollapseClause &clause) {
701 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
702 llvm::APInt value =
703 clause.getIntExpr()->EvaluateKnownConstInt(Ctx: cgf.cgm.getASTContext());
704
705 value = value.sextOrTrunc(width: 64);
706 operation.setCollapseForDeviceTypes(builder.getContext(),
707 lastDeviceTypeValues, value);
708 } else if constexpr (isCombinedType<OpTy>) {
709 applyToLoopOp(clause);
710 } else {
711 llvm_unreachable("Unknown construct kind in VisitCollapseClause");
712 }
713 }
714
715 void VisitTileClause(const OpenACCTileClause &clause) {
716 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
717 llvm::SmallVector<mlir::Value> values;
718
719 for (const Expr *e : clause.getSizeExprs()) {
720 mlir::Location exprLoc = cgf.cgm.getLoc(e->getBeginLoc());
721
722 // We represent the * as -1. Additionally, this is a constant, so we
723 // can always just emit it as 64 bits to avoid having to do any more
724 // work to determine signedness or size.
725 if (isa<OpenACCAsteriskSizeExpr>(Val: e)) {
726 values.push_back(createConstantInt(exprLoc, 64, -1));
727 } else {
728 llvm::APInt curValue =
729 e->EvaluateKnownConstInt(Ctx: cgf.cgm.getASTContext());
730 values.push_back(createConstantInt(
731 exprLoc, 64, curValue.sextOrTrunc(64).getSExtValue()));
732 }
733 }
734
735 operation.setTileForDeviceTypes(builder.getContext(),
736 lastDeviceTypeValues, values);
737 } else if constexpr (isCombinedType<OpTy>) {
738 applyToLoopOp(clause);
739 } else {
740 llvm_unreachable("Unknown construct kind in VisitTileClause");
741 }
742 }
743
744 void VisitWorkerClause(const OpenACCWorkerClause &clause) {
745 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
746 if (clause.hasIntExpr())
747 operation.addWorkerNumOperand(builder.getContext(),
748 emitIntExpr(clause.getIntExpr()),
749 lastDeviceTypeValues);
750 else
751 operation.addEmptyWorker(builder.getContext(), lastDeviceTypeValues);
752
753 } else if constexpr (isCombinedType<OpTy>) {
754 applyToLoopOp(clause);
755 } else {
756 // TODO: When we've implemented this for everything, switch this to an
757 // unreachable. Combined constructs remain.
758 return clauseNotImplemented(c: clause);
759 }
760 }
761
762 void VisitVectorClause(const OpenACCVectorClause &clause) {
763 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
764 if (clause.hasIntExpr())
765 operation.addVectorOperand(builder.getContext(),
766 emitIntExpr(clause.getIntExpr()),
767 lastDeviceTypeValues);
768 else
769 operation.addEmptyVector(builder.getContext(), lastDeviceTypeValues);
770
771 } else if constexpr (isCombinedType<OpTy>) {
772 applyToLoopOp(clause);
773 } else {
774 // TODO: When we've implemented this for everything, switch this to an
775 // unreachable. Combined constructs remain.
776 return clauseNotImplemented(c: clause);
777 }
778 }
779
780 void VisitGangClause(const OpenACCGangClause &clause) {
781 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
782 if (clause.getNumExprs() == 0) {
783 operation.addEmptyGang(builder.getContext(), lastDeviceTypeValues);
784 } else {
785 llvm::SmallVector<mlir::Value> values;
786 llvm::SmallVector<mlir::acc::GangArgType> argTypes;
787 for (unsigned i : llvm::index_range(0u, clause.getNumExprs())) {
788 auto [kind, expr] = clause.getExpr(I: i);
789 mlir::Location exprLoc = cgf.cgm.getLoc(expr->getBeginLoc());
790 argTypes.push_back(decodeGangType(kind));
791 if (kind == OpenACCGangKind::Dim) {
792 llvm::APInt curValue =
793 expr->EvaluateKnownConstInt(Ctx: cgf.cgm.getASTContext());
794 // The value is 1, 2, or 3, but the type isn't necessarily smaller
795 // than 64.
796 curValue = curValue.sextOrTrunc(width: 64);
797 values.push_back(
798 createConstantInt(exprLoc, 64, curValue.getSExtValue()));
799 } else if (isa<OpenACCAsteriskSizeExpr>(Val: expr)) {
800 values.push_back(createConstantInt(exprLoc, 64, -1));
801 } else {
802 values.push_back(emitIntExpr(expr));
803 }
804 }
805
806 operation.addGangOperands(builder.getContext(), lastDeviceTypeValues,
807 argTypes, values);
808 }
809 } else if constexpr (isCombinedType<OpTy>) {
810 applyToLoopOp(clause);
811 } else {
812 llvm_unreachable("Unknown construct kind in VisitGangClause");
813 }
814 }
815
816 void VisitCopyClause(const OpenACCCopyClause &clause) {
817 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
818 mlir::acc::KernelsOp>) {
819 for (auto var : clause.getVarList())
820 addDataOperand<mlir::acc::CopyinOp, mlir::acc::CopyoutOp>(
821 var, mlir::acc::DataClause::acc_copy, /*structured=*/true,
822 /*implicit=*/false);
823 } else if constexpr (isCombinedType<OpTy>) {
824 applyToComputeOp(clause);
825 } else {
826 // TODO: When we've implemented this for everything, switch this to an
827 // unreachable. data, declare, combined constructs remain.
828 return clauseNotImplemented(c: clause);
829 }
830 }
831
832 void VisitUseDeviceClause(const OpenACCUseDeviceClause &clause) {
833 if constexpr (isOneOfTypes<OpTy, mlir::acc::HostDataOp>) {
834 for (auto var : clause.getVarList())
835 addDataOperand<mlir::acc::UseDeviceOp>(
836 var, mlir::acc::DataClause::acc_use_device,
837 /*structured=*/true, /*implicit=*/false);
838 } else {
839 llvm_unreachable("Unknown construct kind in VisitUseDeviceClause");
840 }
841 }
842
843 void VisitDevicePtrClause(const OpenACCDevicePtrClause &clause) {
844 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
845 mlir::acc::KernelsOp>) {
846 for (auto var : clause.getVarList())
847 addDataOperand<mlir::acc::DevicePtrOp>(
848 var, mlir::acc::DataClause::acc_deviceptr, /*structured=*/true,
849 /*implicit=*/false);
850 } else if constexpr (isCombinedType<OpTy>) {
851 applyToComputeOp(clause);
852 } else {
853 // TODO: When we've implemented this for everything, switch this to an
854 // unreachable. data, declare remain.
855 return clauseNotImplemented(c: clause);
856 }
857 }
858
859 void VisitNoCreateClause(const OpenACCNoCreateClause &clause) {
860 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
861 mlir::acc::KernelsOp>) {
862 for (auto var : clause.getVarList())
863 addDataOperand<mlir::acc::NoCreateOp, mlir::acc::DeleteOp>(
864 var, mlir::acc::DataClause::acc_no_create, /*structured=*/true,
865 /*implicit=*/false);
866 } else if constexpr (isCombinedType<OpTy>) {
867 applyToComputeOp(clause);
868 } else {
869 // TODO: When we've implemented this for everything, switch this to an
870 // unreachable. data remains.
871 return clauseNotImplemented(c: clause);
872 }
873 }
874
875 void VisitPresentClause(const OpenACCPresentClause &clause) {
876 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
877 mlir::acc::KernelsOp>) {
878 for (auto var : clause.getVarList())
879 addDataOperand<mlir::acc::PresentOp, mlir::acc::DeleteOp>(
880 var, mlir::acc::DataClause::acc_present, /*structured=*/true,
881 /*implicit=*/false);
882 } else if constexpr (isCombinedType<OpTy>) {
883 applyToComputeOp(clause);
884 } else {
885 // TODO: When we've implemented this for everything, switch this to an
886 // unreachable. data & declare remain.
887 return clauseNotImplemented(c: clause);
888 }
889 }
890
891 void VisitAttachClause(const OpenACCAttachClause &clause) {
892 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
893 mlir::acc::KernelsOp>) {
894 for (auto var : clause.getVarList())
895 addDataOperand<mlir::acc::AttachOp, mlir::acc::DetachOp>(
896 var, mlir::acc::DataClause::acc_attach, /*structured=*/true,
897 /*implicit=*/false);
898 } else if constexpr (isCombinedType<OpTy>) {
899 applyToComputeOp(clause);
900 } else {
901 // TODO: When we've implemented this for everything, switch this to an
902 // unreachable. data, enter data remain.
903 return clauseNotImplemented(c: clause);
904 }
905 }
906};
907
908template <typename OpTy>
909auto makeClauseEmitter(OpTy &op, CIRGen::CIRGenFunction &cgf,
910 CIRGen::CIRGenBuilderTy &builder,
911 OpenACCDirectiveKind dirKind, SourceLocation dirLoc) {
912 return OpenACCClauseCIREmitter<OpTy>(op, cgf, builder, dirKind, dirLoc);
913}
914} // namespace
915
916template <typename Op>
917void CIRGenFunction::emitOpenACCClauses(
918 Op &op, OpenACCDirectiveKind dirKind, SourceLocation dirLoc,
919 ArrayRef<const OpenACCClause *> clauses) {
920 mlir::OpBuilder::InsertionGuard guardCase(builder);
921
922 // Sets insertion point before the 'op', since every new expression needs to
923 // be before the operation.
924 builder.setInsertionPoint(op);
925 makeClauseEmitter(op, *this, builder, dirKind, dirLoc).emitClauses(clauses);
926}
927
928#define EXPL_SPEC(N) \
929 template void CIRGenFunction::emitOpenACCClauses<N>( \
930 N &, OpenACCDirectiveKind, SourceLocation, \
931 ArrayRef<const OpenACCClause *>);
932EXPL_SPEC(mlir::acc::ParallelOp)
933EXPL_SPEC(mlir::acc::SerialOp)
934EXPL_SPEC(mlir::acc::KernelsOp)
935EXPL_SPEC(mlir::acc::LoopOp)
936EXPL_SPEC(mlir::acc::DataOp)
937EXPL_SPEC(mlir::acc::InitOp)
938EXPL_SPEC(mlir::acc::ShutdownOp)
939EXPL_SPEC(mlir::acc::SetOp)
940EXPL_SPEC(mlir::acc::WaitOp)
941EXPL_SPEC(mlir::acc::HostDataOp)
942#undef EXPL_SPEC
943
944template <typename ComputeOp, typename LoopOp>
945void CIRGenFunction::emitOpenACCClauses(
946 ComputeOp &op, LoopOp &loopOp, OpenACCDirectiveKind dirKind,
947 SourceLocation dirLoc, ArrayRef<const OpenACCClause *> clauses) {
948 static_assert(std::is_same_v<mlir::acc::LoopOp, LoopOp>);
949
950 CombinedConstructClauseInfo<ComputeOp> inf{op, loopOp};
951 // We cannot set the insertion point here and do so in the emitter, but make
952 // sure we reset it with the 'guard' anyway.
953 mlir::OpBuilder::InsertionGuard guardCase(builder);
954 makeClauseEmitter(inf, *this, builder, dirKind, dirLoc).emitClauses(clauses);
955}
956
957#define EXPL_SPEC(N) \
958 template void CIRGenFunction::emitOpenACCClauses<N, mlir::acc::LoopOp>( \
959 N &, mlir::acc::LoopOp &, OpenACCDirectiveKind, SourceLocation, \
960 ArrayRef<const OpenACCClause *>);
961
962EXPL_SPEC(mlir::acc::ParallelOp)
963EXPL_SPEC(mlir::acc::SerialOp)
964EXPL_SPEC(mlir::acc::KernelsOp)
965#undef EXPL_SPEC
966

source code of clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp