xref: /freebsd/contrib/llvm-project/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
23 using namespace clang;
24 using namespace clang::CIRGen;
25 
26 namespace {
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.
29 template <typename ToTest, typename T, typename... Tys>
30 constexpr bool isOneOfTypes =
31     std::is_same_v<ToTest, T> || isOneOfTypes<ToTest, Tys...>;
32 template <typename ToTest, typename T>
33 constexpr 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.
38 template <typename CompOpTy> struct CombinedConstructClauseInfo {
39   using ComputeOpTy = CompOpTy;
40   ComputeOpTy computeOp;
41   mlir::acc::LoopOp loopOp;
42 };
43 template <typename ToTest> constexpr bool isCombinedType = false;
44 template <typename T>
45 constexpr bool isCombinedType<CombinedConstructClauseInfo<T>> = true;
46 
47 template <typename OpTy>
48 class 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 
clauseNotImplemented(const OpenACCClause & c)71   void clauseNotImplemented(const OpenACCClause &c) {
72     cgf.cgm.errorNYI(c.getSourceRange(), "OpenACC Clause", c.getClauseKind());
73   }
74 
setLastDeviceTypeClause(const OpenACCDeviceTypeClause & clause)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 
emitIntExpr(const Expr * intExpr)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.
createCondition(const Expr * condExpr)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 
createConstantInt(mlir::Location loc,unsigned width,int64_t value)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 
createConstantInt(SourceLocation loc,unsigned width,int64_t value)113   mlir::Value createConstantInt(SourceLocation loc, unsigned width,
114                                 int64_t value) {
115     return createConstantInt(cgf.cgm.getLoc(loc), width, value);
116   }
117 
decodeDeviceType(const IdentifierInfo * ii)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 
decodeGangType(OpenACCGangKind gk)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>>
applyToLoopOp(const OpenACCClause & c)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>>
applyToComputeOp(const OpenACCClause & c)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
convertModifiers(OpenACCModifierKind modifiers)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(modifiers, 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>
addDataOperand(const Expr * varOperand,mlir::acc::DataClause dataClause,OpenACCModifierKind modifiers,bool structured,bool implicit)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(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>
addDataOperand(const Expr * varOperand,mlir::acc::DataClause dataClause,OpenACCModifierKind modifiers,bool structured,bool implicit)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(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.
getAsyncOnlyAttr()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.
getAsyncOperandsDeviceTypeAttr()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.
getAsyncOperands()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.
updateDataOperandAsyncValues()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 
358 public:
OpenACCClauseCIREmitter(OpTy & operation,CIRGen::CIRGenFunction & cgf,CIRGen::CIRGenBuilderTy & builder,OpenACCDirectiveKind dirKind,SourceLocation dirLoc)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 
VisitClause(const OpenACCClause & clause)365   void VisitClause(const OpenACCClause &clause) {
366     clauseNotImplemented(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.
emitClauses(ArrayRef<const OpenACCClause * > clauses)372   void emitClauses(ArrayRef<const OpenACCClause *> clauses) {
373     this->VisitClauseList(clauses);
374     updateDataOperandAsyncValues();
375   }
376 
VisitDefaultClause(const OpenACCDefaultClause & clause)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 
VisitDeviceTypeClause(const OpenACCDeviceTypeClause & clause)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(clause);
428     }
429   }
430 
VisitNumWorkersClause(const OpenACCNumWorkersClause & clause)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 
VisitVectorLengthClause(const OpenACCVectorLengthClause & clause)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 
VisitAsyncClause(const OpenACCAsyncClause & clause)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(clause);
494     }
495   }
496 
VisitSelfClause(const OpenACCSelfClause & clause)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 
VisitHostClause(const OpenACCHostClause & clause)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 
VisitDeviceClause(const OpenACCDeviceClause & clause)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 
VisitIfClause(const OpenACCIfClause & clause)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 
VisitIfPresentClause(const OpenACCIfPresentClause & clause)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 
VisitDeviceNumClause(const OpenACCDeviceNumClause & clause)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 
VisitNumGangsClause(const OpenACCNumGangsClause & clause)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 
VisitWaitClause(const OpenACCWaitClause & clause)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(clause);
618     }
619   }
620 
VisitDefaultAsyncClause(const OpenACCDefaultAsyncClause & clause)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 
VisitSeqClause(const OpenACCSeqClause & clause)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(clause);
639     }
640   }
641 
VisitAutoClause(const OpenACCAutoClause & clause)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(clause);
651     }
652   }
653 
VisitIndependentClause(const OpenACCIndependentClause & clause)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(clause);
663     }
664   }
665 
VisitCollapseClause(const OpenACCCollapseClause & clause)666   void VisitCollapseClause(const OpenACCCollapseClause &clause) {
667     if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
668       llvm::APInt value =
669           clause.getIntExpr()->EvaluateKnownConstInt(cgf.cgm.getASTContext());
670 
671       value = value.sextOrTrunc(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 
VisitTileClause(const OpenACCTileClause & clause)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>(e)) {
692           values.push_back(createConstantInt(exprLoc, 64, -1));
693         } else {
694           llvm::APInt curValue =
695               e->EvaluateKnownConstInt(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 
VisitWorkerClause(const OpenACCWorkerClause & clause)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(clause);
725     }
726   }
727 
VisitVectorClause(const OpenACCVectorClause & clause)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(clause);
743     }
744   }
745 
VisitGangClause(const OpenACCGangClause & clause)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);
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(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(64);
763             values.push_back(
764                 createConstantInt(exprLoc, 64, curValue.getSExtValue()));
765           } else if (isa<OpenACCAsteriskSizeExpr>(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 
VisitCopyClause(const OpenACCCopyClause & clause)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(clause);
796     }
797   }
798 
VisitCopyInClause(const OpenACCCopyInClause & clause)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(clause);
818     }
819   }
820 
VisitCopyOutClause(const OpenACCCopyOutClause & clause)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(clause);
841     }
842   }
843 
VisitCreateClause(const OpenACCCreateClause & clause)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(clause);
863     }
864   }
865 
VisitDeleteClause(const OpenACCDeleteClause & clause)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 
VisitDetachClause(const OpenACCDetachClause & clause)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 
VisitFinalizeClause(const OpenACCFinalizeClause & clause)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 
VisitUseDeviceClause(const OpenACCUseDeviceClause & clause)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 
VisitDevicePtrClause(const OpenACCDevicePtrClause & clause)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(clause);
923     }
924   }
925 
VisitNoCreateClause(const OpenACCNoCreateClause & clause)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 
VisitPresentClause(const OpenACCPresentClause & clause)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(clause);
953     }
954   }
955 
VisitAttachClause(const OpenACCAttachClause & clause)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 
976 template <typename OpTy>
makeClauseEmitter(OpTy & op,CIRGen::CIRGenFunction & cgf,CIRGen::CIRGenBuilderTy & builder,OpenACCDirectiveKind dirKind,SourceLocation dirLoc)977 auto 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 
984 template <typename Op>
emitOpenACCClauses(Op & op,OpenACCDirectiveKind dirKind,SourceLocation dirLoc,ArrayRef<const OpenACCClause * > clauses)985 void 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 *>);
1000 EXPL_SPEC(mlir::acc::ParallelOp)
EXPL_SPEC(mlir::acc::SerialOp)1001 EXPL_SPEC(mlir::acc::SerialOp)
1002 EXPL_SPEC(mlir::acc::KernelsOp)
1003 EXPL_SPEC(mlir::acc::LoopOp)
1004 EXPL_SPEC(mlir::acc::DataOp)
1005 EXPL_SPEC(mlir::acc::InitOp)
1006 EXPL_SPEC(mlir::acc::ShutdownOp)
1007 EXPL_SPEC(mlir::acc::SetOp)
1008 EXPL_SPEC(mlir::acc::WaitOp)
1009 EXPL_SPEC(mlir::acc::HostDataOp)
1010 EXPL_SPEC(mlir::acc::EnterDataOp)
1011 EXPL_SPEC(mlir::acc::ExitDataOp)
1012 EXPL_SPEC(mlir::acc::UpdateOp)
1013 #undef EXPL_SPEC
1014 
1015 template <typename ComputeOp, typename LoopOp>
1016 void 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 
1033 EXPL_SPEC(mlir::acc::ParallelOp)
1034 EXPL_SPEC(mlir::acc::SerialOp)
1035 EXPL_SPEC(mlir::acc::KernelsOp)
1036 #undef EXPL_SPEC
1037