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