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 for (const DeviceTypeArgument &arg : clause.getArchitectures())
79 lastDeviceTypeValues.push_back(decodeDeviceType(arg.getIdentifierInfo()));
80 }
81
82 mlir::Value emitIntExpr(const Expr *intExpr) {
83 return cgf.emitOpenACCIntExpr(intExpr);
84 }
85
86 // 'condition' as an OpenACC grammar production is used for 'if' and (some
87 // variants of) 'self'. It needs to be emitted as a signless-1-bit value, so
88 // this function emits the expression, then sets the unrealized conversion
89 // cast correctly, and returns the completed value.
90 mlir::Value createCondition(const Expr *condExpr) {
91 mlir::Value condition = cgf.evaluateExprAsBool(condExpr);
92 mlir::Location exprLoc = cgf.cgm.getLoc(condExpr->getBeginLoc());
93 mlir::IntegerType targetType = mlir::IntegerType::get(
94 &cgf.getMLIRContext(), /*width=*/1,
95 mlir::IntegerType::SignednessSemantics::Signless);
96 auto conversionOp = builder.create<mlir::UnrealizedConversionCastOp>(
97 exprLoc, targetType, condition);
98 return conversionOp.getResult(0);
99 }
100
101 mlir::Value createConstantInt(mlir::Location loc, unsigned width,
102 int64_t value) {
103 return cgf.createOpenACCConstantInt(loc, width, value);
104 mlir::IntegerType ty = mlir::IntegerType::get(
105 &cgf.getMLIRContext(), width,
106 mlir::IntegerType::SignednessSemantics::Signless);
107 auto constOp = builder.create<mlir::arith::ConstantOp>(
108 loc, builder.getIntegerAttr(ty, value));
109
110 return constOp.getResult();
111 }
112
113 mlir::Value createConstantInt(SourceLocation loc, unsigned width,
114 int64_t value) {
115 return createConstantInt(cgf.cgm.getLoc(loc), width, value);
116 }
117
118 mlir::acc::DeviceType decodeDeviceType(const IdentifierInfo *ii) {
119 // '*' case leaves no identifier-info, just a nullptr.
120 if (!ii)
121 return mlir::acc::DeviceType::Star;
122 return llvm::StringSwitch<mlir::acc::DeviceType>(ii->getName())
123 .CaseLower("default", mlir::acc::DeviceType::Default)
124 .CaseLower("host", mlir::acc::DeviceType::Host)
125 .CaseLower("multicore", mlir::acc::DeviceType::Multicore)
126 .CasesLower("nvidia", "acc_device_nvidia",
127 mlir::acc::DeviceType::Nvidia)
128 .CaseLower("radeon", mlir::acc::DeviceType::Radeon);
129 }
130
131 mlir::acc::GangArgType decodeGangType(OpenACCGangKind gk) {
132 switch (gk) {
133 case OpenACCGangKind::Num:
134 return mlir::acc::GangArgType::Num;
135 case OpenACCGangKind::Dim:
136 return mlir::acc::GangArgType::Dim;
137 case OpenACCGangKind::Static:
138 return mlir::acc::GangArgType::Static;
139 }
140 llvm_unreachable("unknown gang kind");
141 }
142
143 template <typename U = void,
144 typename = std::enable_if_t<isCombinedType<OpTy>, U>>
145 void applyToLoopOp(const OpenACCClause &c) {
146 mlir::OpBuilder::InsertionGuard guardCase(builder);
147 builder.setInsertionPoint(operation.loopOp);
148 OpenACCClauseCIREmitter<mlir::acc::LoopOp> loopEmitter{
149 operation.loopOp, cgf, builder, dirKind, dirLoc};
150 loopEmitter.lastDeviceTypeValues = lastDeviceTypeValues;
151 loopEmitter.Visit(&c);
152 }
153
154 template <typename U = void,
155 typename = std::enable_if_t<isCombinedType<OpTy>, U>>
156 void applyToComputeOp(const OpenACCClause &c) {
157 mlir::OpBuilder::InsertionGuard guardCase(builder);
158 builder.setInsertionPoint(operation.computeOp);
159 OpenACCClauseCIREmitter<typename OpTy::ComputeOpTy> computeEmitter{
160 operation.computeOp, cgf, builder, dirKind, dirLoc};
161
162 computeEmitter.lastDeviceTypeValues = lastDeviceTypeValues;
163
164 // Async handler uses the first data operand to figure out where to insert
165 // its information if it is present. This ensures that the new handler will
166 // correctly set the insertion point for async.
167 if (!dataOperands.empty())
168 computeEmitter.dataOperands.push_back(dataOperands.front());
169 computeEmitter.Visit(&c);
170
171 // Make sure all of the new data operands are kept track of here. The
172 // combined constructs always apply 'async' to only the compute component,
173 // so we need to collect these.
174 dataOperands.append(computeEmitter.dataOperands);
175 }
176
177 mlir::acc::DataClauseModifier
178 convertModifiers(OpenACCModifierKind modifiers) {
179 using namespace mlir::acc;
180 static_assert(static_cast<int>(OpenACCModifierKind::Zero) ==
181 static_cast<int>(DataClauseModifier::zero) &&
182 static_cast<int>(OpenACCModifierKind::Readonly) ==
183 static_cast<int>(DataClauseModifier::readonly) &&
184 static_cast<int>(OpenACCModifierKind::AlwaysIn) ==
185 static_cast<int>(DataClauseModifier::alwaysin) &&
186 static_cast<int>(OpenACCModifierKind::AlwaysOut) ==
187 static_cast<int>(DataClauseModifier::alwaysout) &&
188 static_cast<int>(OpenACCModifierKind::Capture) ==
189 static_cast<int>(DataClauseModifier::capture));
190
191 DataClauseModifier mlirModifiers{};
192
193 // The MLIR representation of this represents `always` as `alwaysin` +
194 // `alwaysout`. So do a small fixup here.
195 if (isOpenACCModifierBitSet(List: modifiers, Bit: OpenACCModifierKind::Always)) {
196 mlirModifiers = mlirModifiers | DataClauseModifier::always;
197 modifiers &= ~OpenACCModifierKind::Always;
198 }
199
200 mlirModifiers = mlirModifiers | static_cast<DataClauseModifier>(modifiers);
201 return mlirModifiers;
202 }
203
204 template <typename BeforeOpTy, typename AfterOpTy>
205 void addDataOperand(const Expr *varOperand, mlir::acc::DataClause dataClause,
206 OpenACCModifierKind modifiers, bool structured,
207 bool implicit) {
208 CIRGenFunction::OpenACCDataOperandInfo opInfo =
209 cgf.getOpenACCDataOperandInfo(e: varOperand);
210
211 auto beforeOp =
212 builder.create<BeforeOpTy>(opInfo.beginLoc, opInfo.varValue, structured,
213 implicit, opInfo.name, opInfo.bounds);
214 operation.getDataClauseOperandsMutable().append(beforeOp.getResult());
215
216 AfterOpTy afterOp;
217 {
218 mlir::OpBuilder::InsertionGuard guardCase(builder);
219 builder.setInsertionPointAfter(operation);
220
221 if constexpr (std::is_same_v<AfterOpTy, mlir::acc::DeleteOp> ||
222 std::is_same_v<AfterOpTy, mlir::acc::DetachOp>) {
223 // Detach/Delete ops don't have the variable reference here, so they
224 // take 1 fewer argument to their build function.
225 afterOp = builder.create<AfterOpTy>(
226 opInfo.beginLoc, beforeOp.getResult(), structured, implicit,
227 opInfo.name, opInfo.bounds);
228 } else {
229 afterOp = builder.create<AfterOpTy>(
230 opInfo.beginLoc, beforeOp.getResult(), opInfo.varValue, structured,
231 implicit, opInfo.name, opInfo.bounds);
232 }
233 }
234
235 // Set the 'rest' of the info for both operations.
236 beforeOp.setDataClause(dataClause);
237 afterOp.setDataClause(dataClause);
238 beforeOp.setModifiers(convertModifiers(modifiers));
239 afterOp.setModifiers(convertModifiers(modifiers));
240
241 // Make sure we record these, so 'async' values can be updated later.
242 dataOperands.push_back(beforeOp.getOperation());
243 dataOperands.push_back(afterOp.getOperation());
244 }
245
246 template <typename BeforeOpTy>
247 void addDataOperand(const Expr *varOperand, mlir::acc::DataClause dataClause,
248 OpenACCModifierKind modifiers, bool structured,
249 bool implicit) {
250 CIRGenFunction::OpenACCDataOperandInfo opInfo =
251 cgf.getOpenACCDataOperandInfo(e: varOperand);
252 auto beforeOp =
253 builder.create<BeforeOpTy>(opInfo.beginLoc, opInfo.varValue, structured,
254 implicit, opInfo.name, opInfo.bounds);
255 operation.getDataClauseOperandsMutable().append(beforeOp.getResult());
256
257 // Set the 'rest' of the info for the operation.
258 beforeOp.setDataClause(dataClause);
259 beforeOp.setModifiers(convertModifiers(modifiers));
260
261 // Make sure we record these, so 'async' values can be updated later.
262 dataOperands.push_back(beforeOp.getOperation());
263 }
264
265 // Helper function that covers for the fact that we don't have this function
266 // on all operation types.
267 mlir::ArrayAttr getAsyncOnlyAttr() {
268 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
269 mlir::acc::KernelsOp, mlir::acc::DataOp,
270 mlir::acc::UpdateOp>) {
271 return operation.getAsyncOnlyAttr();
272 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp,
273 mlir::acc::ExitDataOp>) {
274 if (!operation.getAsyncAttr())
275 return mlir::ArrayAttr{};
276
277 llvm::SmallVector<mlir::Attribute> devTysTemp;
278 devTysTemp.push_back(mlir::acc::DeviceTypeAttr::get(
279 builder.getContext(), mlir::acc::DeviceType::None));
280 return mlir::ArrayAttr::get(builder.getContext(), devTysTemp);
281 } else if constexpr (isCombinedType<OpTy>) {
282 return operation.computeOp.getAsyncOnlyAttr();
283 }
284
285 // Note: 'wait' has async as well, but it cannot have data clauses, so we
286 // don't have to handle them here.
287
288 llvm_unreachable("getting asyncOnly when clause not valid on operation?");
289 }
290
291 // Helper function that covers for the fact that we don't have this function
292 // on all operation types.
293 mlir::ArrayAttr getAsyncOperandsDeviceTypeAttr() {
294 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
295 mlir::acc::KernelsOp, mlir::acc::DataOp,
296 mlir::acc::UpdateOp>) {
297 return operation.getAsyncOperandsDeviceTypeAttr();
298 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp,
299 mlir::acc::ExitDataOp>) {
300 if (!operation.getAsyncOperand())
301 return mlir::ArrayAttr{};
302
303 llvm::SmallVector<mlir::Attribute> devTysTemp;
304 devTysTemp.push_back(mlir::acc::DeviceTypeAttr::get(
305 builder.getContext(), mlir::acc::DeviceType::None));
306 return mlir::ArrayAttr::get(builder.getContext(), devTysTemp);
307 } else if constexpr (isCombinedType<OpTy>) {
308 return operation.computeOp.getAsyncOperandsDeviceTypeAttr();
309 }
310
311 // Note: 'wait' has async as well, but it cannot have data clauses, so we
312 // don't have to handle them here.
313
314 llvm_unreachable(
315 "getting asyncOperandsDeviceType when clause not valid on operation?");
316 }
317
318 // Helper function that covers for the fact that we don't have this function
319 // on all operation types.
320 mlir::OperandRange getAsyncOperands() {
321 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
322 mlir::acc::KernelsOp, mlir::acc::DataOp,
323 mlir::acc::UpdateOp>)
324 return operation.getAsyncOperands();
325 else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp,
326 mlir::acc::ExitDataOp>)
327 return operation.getAsyncOperandMutable();
328 else if constexpr (isCombinedType<OpTy>)
329 return operation.computeOp.getAsyncOperands();
330
331 // Note: 'wait' has async as well, but it cannot have data clauses, so we
332 // don't have to handle them here.
333
334 llvm_unreachable(
335 "getting asyncOperandsDeviceType when clause not valid on operation?");
336 }
337
338 // The 'data' clauses all require that we add the 'async' values from the
339 // operation to them. We've collected the data operands along the way, so use
340 // that list to get the current 'async' values.
341 void updateDataOperandAsyncValues() {
342 if (!hasAsyncClause || dataOperands.empty())
343 return;
344
345 for (mlir::Operation *dataOp : dataOperands) {
346 llvm::TypeSwitch<mlir::Operation *, void>(dataOp)
347 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto op) {
348 op.setAsyncOnlyAttr(getAsyncOnlyAttr());
349 op.setAsyncOperandsDeviceTypeAttr(getAsyncOperandsDeviceTypeAttr());
350 op.getAsyncOperandsMutable().assign(getAsyncOperands());
351 })
352 .Default([&](mlir::Operation *) {
353 llvm_unreachable("Not a data operation?");
354 });
355 }
356 }
357
358public:
359 OpenACCClauseCIREmitter(OpTy &operation, CIRGen::CIRGenFunction &cgf,
360 CIRGen::CIRGenBuilderTy &builder,
361 OpenACCDirectiveKind dirKind, SourceLocation dirLoc)
362 : operation(operation), cgf(cgf), builder(builder), dirKind(dirKind),
363 dirLoc(dirLoc) {}
364
365 void VisitClause(const OpenACCClause &clause) {
366 clauseNotImplemented(c: clause);
367 }
368
369 // The entry point for the CIR emitter. All users should use this rather than
370 // 'visitClauseList', as this also handles the things that have to happen
371 // 'after' the clauses are all visited.
372 void emitClauses(ArrayRef<const OpenACCClause *> clauses) {
373 this->VisitClauseList(clauses);
374 updateDataOperandAsyncValues();
375 }
376
377 void VisitDefaultClause(const OpenACCDefaultClause &clause) {
378 // This type-trait checks if 'op'(the first arg) is one of the mlir::acc
379 // operations listed in the rest of the arguments.
380 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
381 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
382 switch (clause.getDefaultClauseKind()) {
383 case OpenACCDefaultClauseKind::None:
384 operation.setDefaultAttr(mlir::acc::ClauseDefaultValue::None);
385 break;
386 case OpenACCDefaultClauseKind::Present:
387 operation.setDefaultAttr(mlir::acc::ClauseDefaultValue::Present);
388 break;
389 case OpenACCDefaultClauseKind::Invalid:
390 break;
391 }
392 } else if constexpr (isCombinedType<OpTy>) {
393 applyToComputeOp(clause);
394 } else {
395 llvm_unreachable("Unknown construct kind in VisitDefaultClause");
396 }
397 }
398
399 void VisitDeviceTypeClause(const OpenACCDeviceTypeClause &clause) {
400 setLastDeviceTypeClause(clause);
401
402 if constexpr (isOneOfTypes<OpTy, mlir::acc::InitOp,
403 mlir::acc::ShutdownOp>) {
404 for (const DeviceTypeArgument &arg : clause.getArchitectures())
405 operation.addDeviceType(builder.getContext(),
406 decodeDeviceType(arg.getIdentifierInfo()));
407 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::SetOp>) {
408 assert(!operation.getDeviceTypeAttr() && "already have device-type?");
409 assert(clause.getArchitectures().size() <= 1);
410
411 if (!clause.getArchitectures().empty())
412 operation.setDeviceType(
413 decodeDeviceType(clause.getArchitectures()[0].getIdentifierInfo()));
414 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
415 mlir::acc::SerialOp, mlir::acc::KernelsOp,
416 mlir::acc::DataOp, mlir::acc::LoopOp,
417 mlir::acc::UpdateOp>) {
418 // Nothing to do here, these constructs don't have any IR for these, as
419 // they just modify the other clauses IR. So setting of
420 // `lastDeviceTypeValues` (done above) is all we need.
421 } else if constexpr (isCombinedType<OpTy>) {
422 // Nothing to do here either, combined constructs are just going to use
423 // 'lastDeviceTypeValues' to set the value for the child visitor.
424 } else {
425 // TODO: When we've implemented this for everything, switch this to an
426 // unreachable. routine construct remains.
427 return clauseNotImplemented(c: clause);
428 }
429 }
430
431 void VisitNumWorkersClause(const OpenACCNumWorkersClause &clause) {
432 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
433 mlir::acc::KernelsOp>) {
434 operation.addNumWorkersOperand(builder.getContext(),
435 emitIntExpr(clause.getIntExpr()),
436 lastDeviceTypeValues);
437 } else if constexpr (isCombinedType<OpTy>) {
438 applyToComputeOp(clause);
439 } else {
440 llvm_unreachable("Unknown construct kind in VisitNumGangsClause");
441 }
442 }
443
444 void VisitVectorLengthClause(const OpenACCVectorLengthClause &clause) {
445 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
446 mlir::acc::KernelsOp>) {
447 operation.addVectorLengthOperand(builder.getContext(),
448 emitIntExpr(clause.getIntExpr()),
449 lastDeviceTypeValues);
450 } else if constexpr (isCombinedType<OpTy>) {
451 applyToComputeOp(clause);
452 } else {
453 llvm_unreachable("Unknown construct kind in VisitVectorLengthClause");
454 }
455 }
456
457 void VisitAsyncClause(const OpenACCAsyncClause &clause) {
458 hasAsyncClause = true;
459 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
460 mlir::acc::KernelsOp, mlir::acc::DataOp,
461 mlir::acc::EnterDataOp, mlir::acc::ExitDataOp,
462 mlir::acc::UpdateOp>) {
463 if (!clause.hasIntExpr()) {
464 operation.addAsyncOnly(builder.getContext(), lastDeviceTypeValues);
465 } else {
466
467 mlir::Value intExpr;
468 {
469 // Async int exprs can be referenced by the data operands, which means
470 // that the int-exprs have to appear before them. IF there is a data
471 // operand already, set the insertion point to 'before' it.
472 mlir::OpBuilder::InsertionGuard guardCase(builder);
473 if (!dataOperands.empty())
474 builder.setInsertionPoint(dataOperands.front());
475 intExpr = emitIntExpr(clause.getIntExpr());
476 }
477 operation.addAsyncOperand(builder.getContext(), intExpr,
478 lastDeviceTypeValues);
479 }
480 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::WaitOp>) {
481 // Wait doesn't have a device_type, so its handling here is slightly
482 // different.
483 if (!clause.hasIntExpr())
484 operation.setAsync(true);
485 else
486 operation.getAsyncOperandMutable().append(
487 emitIntExpr(clause.getIntExpr()));
488 } else if constexpr (isCombinedType<OpTy>) {
489 applyToComputeOp(clause);
490 } else {
491 // TODO: When we've implemented this for everything, switch this to an
492 // unreachable. Combined constructs remain. update construct remains.
493 return clauseNotImplemented(c: clause);
494 }
495 }
496
497 void VisitSelfClause(const OpenACCSelfClause &clause) {
498 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
499 mlir::acc::KernelsOp>) {
500 if (clause.isEmptySelfClause()) {
501 operation.setSelfAttr(true);
502 } else if (clause.isConditionExprClause()) {
503 assert(clause.hasConditionExpr());
504 operation.getSelfCondMutable().append(
505 createCondition(clause.getConditionExpr()));
506 } else {
507 llvm_unreachable("var-list version of self shouldn't get here");
508 }
509 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::UpdateOp>) {
510 assert(!clause.isEmptySelfClause() && !clause.isConditionExprClause() &&
511 "var-list version of self required for update");
512 for (const Expr *var : clause.getVarList())
513 addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::UpdateHostOp>(
514 var, mlir::acc::DataClause::acc_update_self, {},
515 /*structured=*/false, /*implicit=*/false);
516 } else if constexpr (isCombinedType<OpTy>) {
517 applyToComputeOp(clause);
518 } else {
519 llvm_unreachable("Unknown construct kind in VisitSelfClause");
520 }
521 }
522
523 void VisitHostClause(const OpenACCHostClause &clause) {
524 if constexpr (isOneOfTypes<OpTy, mlir::acc::UpdateOp>) {
525 for (const Expr *var : clause.getVarList())
526 addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::UpdateHostOp>(
527 var, mlir::acc::DataClause::acc_update_host, {},
528 /*structured=*/false, /*implicit=*/false);
529 } else {
530 llvm_unreachable("Unknown construct kind in VisitHostClause");
531 }
532 }
533
534 void VisitDeviceClause(const OpenACCDeviceClause &clause) {
535 if constexpr (isOneOfTypes<OpTy, mlir::acc::UpdateOp>) {
536 for (const Expr *var : clause.getVarList())
537 addDataOperand<mlir::acc::UpdateDeviceOp>(
538 var, mlir::acc::DataClause::acc_update_device, {},
539 /*structured=*/false, /*implicit=*/false);
540 } else {
541 llvm_unreachable("Unknown construct kind in VisitDeviceClause");
542 }
543 }
544
545 void VisitIfClause(const OpenACCIfClause &clause) {
546 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
547 mlir::acc::KernelsOp, mlir::acc::InitOp,
548 mlir::acc::ShutdownOp, mlir::acc::SetOp,
549 mlir::acc::DataOp, mlir::acc::WaitOp,
550 mlir::acc::HostDataOp, mlir::acc::EnterDataOp,
551 mlir::acc::ExitDataOp, mlir::acc::UpdateOp>) {
552 operation.getIfCondMutable().append(
553 createCondition(clause.getConditionExpr()));
554 } else if constexpr (isCombinedType<OpTy>) {
555 applyToComputeOp(clause);
556 } else {
557 llvm_unreachable("Unknown construct kind in VisitIfClause");
558 }
559 }
560
561 void VisitIfPresentClause(const OpenACCIfPresentClause &clause) {
562 if constexpr (isOneOfTypes<OpTy, mlir::acc::HostDataOp,
563 mlir::acc::UpdateOp>) {
564 operation.setIfPresent(true);
565 } else {
566 llvm_unreachable("unknown construct kind in VisitIfPresentClause");
567 }
568 }
569
570 void VisitDeviceNumClause(const OpenACCDeviceNumClause &clause) {
571 if constexpr (isOneOfTypes<OpTy, mlir::acc::InitOp, mlir::acc::ShutdownOp,
572 mlir::acc::SetOp>) {
573 operation.getDeviceNumMutable().append(emitIntExpr(clause.getIntExpr()));
574 } else {
575 llvm_unreachable(
576 "init, shutdown, set, are only valid device_num constructs");
577 }
578 }
579
580 void VisitNumGangsClause(const OpenACCNumGangsClause &clause) {
581 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
582 mlir::acc::KernelsOp>) {
583 llvm::SmallVector<mlir::Value> values;
584 for (const Expr *E : clause.getIntExprs())
585 values.push_back(emitIntExpr(E));
586
587 operation.addNumGangsOperands(builder.getContext(), values,
588 lastDeviceTypeValues);
589 } else if constexpr (isCombinedType<OpTy>) {
590 applyToComputeOp(clause);
591 } else {
592 llvm_unreachable("Unknown construct kind in VisitNumGangsClause");
593 }
594 }
595
596 void VisitWaitClause(const OpenACCWaitClause &clause) {
597 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
598 mlir::acc::KernelsOp, mlir::acc::DataOp,
599 mlir::acc::EnterDataOp, mlir::acc::ExitDataOp,
600 mlir::acc::UpdateOp>) {
601 if (!clause.hasExprs()) {
602 operation.addWaitOnly(builder.getContext(), lastDeviceTypeValues);
603 } else {
604 llvm::SmallVector<mlir::Value> values;
605 if (clause.hasDevNumExpr())
606 values.push_back(emitIntExpr(clause.getDevNumExpr()));
607 for (const Expr *E : clause.getQueueIdExprs())
608 values.push_back(emitIntExpr(E));
609 operation.addWaitOperands(builder.getContext(), clause.hasDevNumExpr(),
610 values, lastDeviceTypeValues);
611 }
612 } else if constexpr (isCombinedType<OpTy>) {
613 applyToComputeOp(clause);
614 } else {
615 // TODO: When we've implemented this for everything, switch this to an
616 // unreachable. update construct remains.
617 return clauseNotImplemented(c: clause);
618 }
619 }
620
621 void VisitDefaultAsyncClause(const OpenACCDefaultAsyncClause &clause) {
622 if constexpr (isOneOfTypes<OpTy, mlir::acc::SetOp>) {
623 operation.getDefaultAsyncMutable().append(
624 emitIntExpr(clause.getIntExpr()));
625 } else {
626 llvm_unreachable("set, is only valid device_num constructs");
627 }
628 }
629
630 void VisitSeqClause(const OpenACCSeqClause &clause) {
631 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
632 operation.addSeq(builder.getContext(), lastDeviceTypeValues);
633 } else if constexpr (isCombinedType<OpTy>) {
634 applyToLoopOp(clause);
635 } else {
636 // TODO: When we've implemented this for everything, switch this to an
637 // unreachable. Routine construct remains.
638 return clauseNotImplemented(c: clause);
639 }
640 }
641
642 void VisitAutoClause(const OpenACCAutoClause &clause) {
643 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
644 operation.addAuto(builder.getContext(), lastDeviceTypeValues);
645 } else if constexpr (isCombinedType<OpTy>) {
646 applyToLoopOp(clause);
647 } else {
648 // TODO: When we've implemented this for everything, switch this to an
649 // unreachable. Routine, construct remains.
650 return clauseNotImplemented(c: clause);
651 }
652 }
653
654 void VisitIndependentClause(const OpenACCIndependentClause &clause) {
655 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
656 operation.addIndependent(builder.getContext(), lastDeviceTypeValues);
657 } else if constexpr (isCombinedType<OpTy>) {
658 applyToLoopOp(clause);
659 } else {
660 // TODO: When we've implemented this for everything, switch this to an
661 // unreachable. Routine construct remains.
662 return clauseNotImplemented(c: clause);
663 }
664 }
665
666 void VisitCollapseClause(const OpenACCCollapseClause &clause) {
667 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
668 llvm::APInt value =
669 clause.getIntExpr()->EvaluateKnownConstInt(Ctx: cgf.cgm.getASTContext());
670
671 value = value.sextOrTrunc(width: 64);
672 operation.setCollapseForDeviceTypes(builder.getContext(),
673 lastDeviceTypeValues, value);
674 } else if constexpr (isCombinedType<OpTy>) {
675 applyToLoopOp(clause);
676 } else {
677 llvm_unreachable("Unknown construct kind in VisitCollapseClause");
678 }
679 }
680
681 void VisitTileClause(const OpenACCTileClause &clause) {
682 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
683 llvm::SmallVector<mlir::Value> values;
684
685 for (const Expr *e : clause.getSizeExprs()) {
686 mlir::Location exprLoc = cgf.cgm.getLoc(e->getBeginLoc());
687
688 // We represent the * as -1. Additionally, this is a constant, so we
689 // can always just emit it as 64 bits to avoid having to do any more
690 // work to determine signedness or size.
691 if (isa<OpenACCAsteriskSizeExpr>(Val: e)) {
692 values.push_back(createConstantInt(exprLoc, 64, -1));
693 } else {
694 llvm::APInt curValue =
695 e->EvaluateKnownConstInt(Ctx: cgf.cgm.getASTContext());
696 values.push_back(createConstantInt(
697 exprLoc, 64, curValue.sextOrTrunc(64).getSExtValue()));
698 }
699 }
700
701 operation.setTileForDeviceTypes(builder.getContext(),
702 lastDeviceTypeValues, values);
703 } else if constexpr (isCombinedType<OpTy>) {
704 applyToLoopOp(clause);
705 } else {
706 llvm_unreachable("Unknown construct kind in VisitTileClause");
707 }
708 }
709
710 void VisitWorkerClause(const OpenACCWorkerClause &clause) {
711 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
712 if (clause.hasIntExpr())
713 operation.addWorkerNumOperand(builder.getContext(),
714 emitIntExpr(clause.getIntExpr()),
715 lastDeviceTypeValues);
716 else
717 operation.addEmptyWorker(builder.getContext(), lastDeviceTypeValues);
718
719 } else if constexpr (isCombinedType<OpTy>) {
720 applyToLoopOp(clause);
721 } else {
722 // TODO: When we've implemented this for everything, switch this to an
723 // unreachable. Combined constructs remain.
724 return clauseNotImplemented(c: clause);
725 }
726 }
727
728 void VisitVectorClause(const OpenACCVectorClause &clause) {
729 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
730 if (clause.hasIntExpr())
731 operation.addVectorOperand(builder.getContext(),
732 emitIntExpr(clause.getIntExpr()),
733 lastDeviceTypeValues);
734 else
735 operation.addEmptyVector(builder.getContext(), lastDeviceTypeValues);
736
737 } else if constexpr (isCombinedType<OpTy>) {
738 applyToLoopOp(clause);
739 } else {
740 // TODO: When we've implemented this for everything, switch this to an
741 // unreachable. Combined constructs remain.
742 return clauseNotImplemented(c: clause);
743 }
744 }
745
746 void VisitGangClause(const OpenACCGangClause &clause) {
747 if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
748 if (clause.getNumExprs() == 0) {
749 operation.addEmptyGang(builder.getContext(), lastDeviceTypeValues);
750 } else {
751 llvm::SmallVector<mlir::Value> values;
752 llvm::SmallVector<mlir::acc::GangArgType> argTypes;
753 for (unsigned i : llvm::index_range(0u, clause.getNumExprs())) {
754 auto [kind, expr] = clause.getExpr(I: i);
755 mlir::Location exprLoc = cgf.cgm.getLoc(expr->getBeginLoc());
756 argTypes.push_back(decodeGangType(kind));
757 if (kind == OpenACCGangKind::Dim) {
758 llvm::APInt curValue =
759 expr->EvaluateKnownConstInt(Ctx: cgf.cgm.getASTContext());
760 // The value is 1, 2, or 3, but the type isn't necessarily smaller
761 // than 64.
762 curValue = curValue.sextOrTrunc(width: 64);
763 values.push_back(
764 createConstantInt(exprLoc, 64, curValue.getSExtValue()));
765 } else if (isa<OpenACCAsteriskSizeExpr>(Val: expr)) {
766 values.push_back(createConstantInt(exprLoc, 64, -1));
767 } else {
768 values.push_back(emitIntExpr(expr));
769 }
770 }
771
772 operation.addGangOperands(builder.getContext(), lastDeviceTypeValues,
773 argTypes, values);
774 }
775 } else if constexpr (isCombinedType<OpTy>) {
776 applyToLoopOp(clause);
777 } else {
778 llvm_unreachable("Unknown construct kind in VisitGangClause");
779 }
780 }
781
782 void VisitCopyClause(const OpenACCCopyClause &clause) {
783 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
784 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
785 for (const Expr *var : clause.getVarList())
786 addDataOperand<mlir::acc::CopyinOp, mlir::acc::CopyoutOp>(
787 var, mlir::acc::DataClause::acc_copy, clause.getModifierList(),
788 /*structured=*/true,
789 /*implicit=*/false);
790 } else if constexpr (isCombinedType<OpTy>) {
791 applyToComputeOp(clause);
792 } else {
793 // TODO: When we've implemented this for everything, switch this to an
794 // unreachable. declare construct remains.
795 return clauseNotImplemented(c: clause);
796 }
797 }
798
799 void VisitCopyInClause(const OpenACCCopyInClause &clause) {
800 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
801 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
802 for (const Expr *var : clause.getVarList())
803 addDataOperand<mlir::acc::CopyinOp, mlir::acc::DeleteOp>(
804 var, mlir::acc::DataClause::acc_copyin, clause.getModifierList(),
805 /*structured=*/true,
806 /*implicit=*/false);
807 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp>) {
808 for (const Expr *var : clause.getVarList())
809 addDataOperand<mlir::acc::CopyinOp>(
810 var, mlir::acc::DataClause::acc_copyin, clause.getModifierList(),
811 /*structured=*/false, /*implicit=*/false);
812 } else if constexpr (isCombinedType<OpTy>) {
813 applyToComputeOp(clause);
814 } else {
815 // TODO: When we've implemented this for everything, switch this to an
816 // unreachable. declare construct remains.
817 return clauseNotImplemented(c: clause);
818 }
819 }
820
821 void VisitCopyOutClause(const OpenACCCopyOutClause &clause) {
822 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
823 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
824 for (const Expr *var : clause.getVarList())
825 addDataOperand<mlir::acc::CreateOp, mlir::acc::CopyoutOp>(
826 var, mlir::acc::DataClause::acc_copyout, clause.getModifierList(),
827 /*structured=*/true,
828 /*implicit=*/false);
829 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {
830 for (const Expr *var : clause.getVarList())
831 addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::CopyoutOp>(
832 var, mlir::acc::DataClause::acc_copyout, clause.getModifierList(),
833 /*structured=*/false,
834 /*implicit=*/false);
835 } else if constexpr (isCombinedType<OpTy>) {
836 applyToComputeOp(clause);
837 } else {
838 // TODO: When we've implemented this for everything, switch this to an
839 // unreachable. declare construct remains.
840 return clauseNotImplemented(c: clause);
841 }
842 }
843
844 void VisitCreateClause(const OpenACCCreateClause &clause) {
845 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
846 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
847 for (const Expr *var : clause.getVarList())
848 addDataOperand<mlir::acc::CreateOp, mlir::acc::DeleteOp>(
849 var, mlir::acc::DataClause::acc_create, clause.getModifierList(),
850 /*structured=*/true,
851 /*implicit=*/false);
852 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp>) {
853 for (const Expr *var : clause.getVarList())
854 addDataOperand<mlir::acc::CreateOp>(
855 var, mlir::acc::DataClause::acc_create, clause.getModifierList(),
856 /*structured=*/false, /*implicit=*/false);
857 } else if constexpr (isCombinedType<OpTy>) {
858 applyToComputeOp(clause);
859 } else {
860 // TODO: When we've implemented this for everything, switch this to an
861 // unreachable. declare construct remains.
862 return clauseNotImplemented(c: clause);
863 }
864 }
865
866 void VisitDeleteClause(const OpenACCDeleteClause &clause) {
867 if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {
868 for (const Expr *var : clause.getVarList())
869 addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::DeleteOp>(
870 var, mlir::acc::DataClause::acc_delete, {},
871 /*structured=*/false,
872 /*implicit=*/false);
873 } else {
874 llvm_unreachable("Unknown construct kind in VisitDeleteClause");
875 }
876 }
877
878 void VisitDetachClause(const OpenACCDetachClause &clause) {
879 if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {
880 for (const Expr *var : clause.getVarList())
881 addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::DetachOp>(
882 var, mlir::acc::DataClause::acc_detach, {},
883 /*structured=*/false,
884 /*implicit=*/false);
885 } else {
886 llvm_unreachable("Unknown construct kind in VisitDetachClause");
887 }
888 }
889
890 void VisitFinalizeClause(const OpenACCFinalizeClause &clause) {
891 if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {
892 operation.setFinalize(true);
893 } else {
894 llvm_unreachable("Unknown construct kind in VisitFinalizeClause");
895 }
896 }
897
898 void VisitUseDeviceClause(const OpenACCUseDeviceClause &clause) {
899 if constexpr (isOneOfTypes<OpTy, mlir::acc::HostDataOp>) {
900 for (const Expr *var : clause.getVarList())
901 addDataOperand<mlir::acc::UseDeviceOp>(
902 var, mlir::acc::DataClause::acc_use_device, {}, /*structured=*/true,
903 /*implicit=*/false);
904 } else {
905 llvm_unreachable("Unknown construct kind in VisitUseDeviceClause");
906 }
907 }
908
909 void VisitDevicePtrClause(const OpenACCDevicePtrClause &clause) {
910 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
911 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
912 for (const Expr *var : clause.getVarList())
913 addDataOperand<mlir::acc::DevicePtrOp>(
914 var, mlir::acc::DataClause::acc_deviceptr, {},
915 /*structured=*/true,
916 /*implicit=*/false);
917 } else if constexpr (isCombinedType<OpTy>) {
918 applyToComputeOp(clause);
919 } else {
920 // TODO: When we've implemented this for everything, switch this to an
921 // unreachable. declare remains.
922 return clauseNotImplemented(c: clause);
923 }
924 }
925
926 void VisitNoCreateClause(const OpenACCNoCreateClause &clause) {
927 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
928 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
929 for (const Expr *var : clause.getVarList())
930 addDataOperand<mlir::acc::NoCreateOp, mlir::acc::DeleteOp>(
931 var, mlir::acc::DataClause::acc_no_create, {}, /*structured=*/true,
932 /*implicit=*/false);
933 } else if constexpr (isCombinedType<OpTy>) {
934 applyToComputeOp(clause);
935 } else {
936 llvm_unreachable("Unknown construct kind in VisitNoCreateClause");
937 }
938 }
939
940 void VisitPresentClause(const OpenACCPresentClause &clause) {
941 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
942 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
943 for (const Expr *var : clause.getVarList())
944 addDataOperand<mlir::acc::PresentOp, mlir::acc::DeleteOp>(
945 var, mlir::acc::DataClause::acc_present, {}, /*structured=*/true,
946 /*implicit=*/false);
947 } else if constexpr (isCombinedType<OpTy>) {
948 applyToComputeOp(clause);
949 } else {
950 // TODO: When we've implemented this for everything, switch this to an
951 // unreachable. declare remains.
952 return clauseNotImplemented(c: clause);
953 }
954 }
955
956 void VisitAttachClause(const OpenACCAttachClause &clause) {
957 if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
958 mlir::acc::KernelsOp, mlir::acc::DataOp>) {
959 for (const Expr *var : clause.getVarList())
960 addDataOperand<mlir::acc::AttachOp, mlir::acc::DetachOp>(
961 var, mlir::acc::DataClause::acc_attach, {}, /*structured=*/true,
962 /*implicit=*/false);
963 } else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp>) {
964 for (const Expr *var : clause.getVarList())
965 addDataOperand<mlir::acc::AttachOp>(
966 var, mlir::acc::DataClause::acc_attach, {},
967 /*structured=*/false, /*implicit=*/false);
968 } else if constexpr (isCombinedType<OpTy>) {
969 applyToComputeOp(clause);
970 } else {
971 llvm_unreachable("Unknown construct kind in VisitAttachClause");
972 }
973 }
974};
975
976template <typename OpTy>
977auto makeClauseEmitter(OpTy &op, CIRGen::CIRGenFunction &cgf,
978 CIRGen::CIRGenBuilderTy &builder,
979 OpenACCDirectiveKind dirKind, SourceLocation dirLoc) {
980 return OpenACCClauseCIREmitter<OpTy>(op, cgf, builder, dirKind, dirLoc);
981}
982} // namespace
983
984template <typename Op>
985void CIRGenFunction::emitOpenACCClauses(
986 Op &op, OpenACCDirectiveKind dirKind, SourceLocation dirLoc,
987 ArrayRef<const OpenACCClause *> clauses) {
988 mlir::OpBuilder::InsertionGuard guardCase(builder);
989
990 // Sets insertion point before the 'op', since every new expression needs to
991 // be before the operation.
992 builder.setInsertionPoint(op);
993 makeClauseEmitter(op, *this, builder, dirKind, dirLoc).emitClauses(clauses);
994}
995
996#define EXPL_SPEC(N) \
997 template void CIRGenFunction::emitOpenACCClauses<N>( \
998 N &, OpenACCDirectiveKind, SourceLocation, \
999 ArrayRef<const OpenACCClause *>);
1000EXPL_SPEC(mlir::acc::ParallelOp)
1001EXPL_SPEC(mlir::acc::SerialOp)
1002EXPL_SPEC(mlir::acc::KernelsOp)
1003EXPL_SPEC(mlir::acc::LoopOp)
1004EXPL_SPEC(mlir::acc::DataOp)
1005EXPL_SPEC(mlir::acc::InitOp)
1006EXPL_SPEC(mlir::acc::ShutdownOp)
1007EXPL_SPEC(mlir::acc::SetOp)
1008EXPL_SPEC(mlir::acc::WaitOp)
1009EXPL_SPEC(mlir::acc::HostDataOp)
1010EXPL_SPEC(mlir::acc::EnterDataOp)
1011EXPL_SPEC(mlir::acc::ExitDataOp)
1012EXPL_SPEC(mlir::acc::UpdateOp)
1013#undef EXPL_SPEC
1014
1015template <typename ComputeOp, typename LoopOp>
1016void CIRGenFunction::emitOpenACCClauses(
1017 ComputeOp &op, LoopOp &loopOp, OpenACCDirectiveKind dirKind,
1018 SourceLocation dirLoc, ArrayRef<const OpenACCClause *> clauses) {
1019 static_assert(std::is_same_v<mlir::acc::LoopOp, LoopOp>);
1020
1021 CombinedConstructClauseInfo<ComputeOp> inf{op, loopOp};
1022 // We cannot set the insertion point here and do so in the emitter, but make
1023 // sure we reset it with the 'guard' anyway.
1024 mlir::OpBuilder::InsertionGuard guardCase(builder);
1025 makeClauseEmitter(inf, *this, builder, dirKind, dirLoc).emitClauses(clauses);
1026}
1027
1028#define EXPL_SPEC(N) \
1029 template void CIRGenFunction::emitOpenACCClauses<N, mlir::acc::LoopOp>( \
1030 N &, mlir::acc::LoopOp &, OpenACCDirectiveKind, SourceLocation, \
1031 ArrayRef<const OpenACCClause *>);
1032
1033EXPL_SPEC(mlir::acc::ParallelOp)
1034EXPL_SPEC(mlir::acc::SerialOp)
1035EXPL_SPEC(mlir::acc::KernelsOp)
1036#undef EXPL_SPEC
1037

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