Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
213799 views
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
71
void clauseNotImplemented(const OpenACCClause &c) {
72
cgf.cgm.errorNYI(c.getSourceRange(), "OpenACC Clause", c.getClauseKind());
73
}
74
75
void setLastDeviceTypeClause(const OpenACCDeviceTypeClause &clause) {
76
lastDeviceTypeValues.clear();
77
78
for (const DeviceTypeArgument &arg : clause.getArchitectures())
79
lastDeviceTypeValues.push_back(decodeDeviceType(arg.getIdentifierInfo()));
80
}
81
82
mlir::Value emitIntExpr(const Expr *intExpr) {
83
return cgf.emitOpenACCIntExpr(intExpr);
84
}
85
86
// 'condition' as an OpenACC grammar production is used for 'if' and (some
87
// variants of) 'self'. It needs to be emitted as a signless-1-bit value, so
88
// this function emits the expression, then sets the unrealized conversion
89
// cast correctly, and returns the completed value.
90
mlir::Value createCondition(const Expr *condExpr) {
91
mlir::Value condition = cgf.evaluateExprAsBool(condExpr);
92
mlir::Location exprLoc = cgf.cgm.getLoc(condExpr->getBeginLoc());
93
mlir::IntegerType targetType = mlir::IntegerType::get(
94
&cgf.getMLIRContext(), /*width=*/1,
95
mlir::IntegerType::SignednessSemantics::Signless);
96
auto conversionOp = builder.create<mlir::UnrealizedConversionCastOp>(
97
exprLoc, targetType, condition);
98
return conversionOp.getResult(0);
99
}
100
101
mlir::Value createConstantInt(mlir::Location loc, unsigned width,
102
int64_t value) {
103
return cgf.createOpenACCConstantInt(loc, width, value);
104
mlir::IntegerType ty = mlir::IntegerType::get(
105
&cgf.getMLIRContext(), width,
106
mlir::IntegerType::SignednessSemantics::Signless);
107
auto constOp = builder.create<mlir::arith::ConstantOp>(
108
loc, builder.getIntegerAttr(ty, value));
109
110
return constOp.getResult();
111
}
112
113
mlir::Value createConstantInt(SourceLocation loc, unsigned width,
114
int64_t value) {
115
return createConstantInt(cgf.cgm.getLoc(loc), width, value);
116
}
117
118
mlir::acc::DeviceType decodeDeviceType(const IdentifierInfo *ii) {
119
// '*' case leaves no identifier-info, just a nullptr.
120
if (!ii)
121
return mlir::acc::DeviceType::Star;
122
return llvm::StringSwitch<mlir::acc::DeviceType>(ii->getName())
123
.CaseLower("default", mlir::acc::DeviceType::Default)
124
.CaseLower("host", mlir::acc::DeviceType::Host)
125
.CaseLower("multicore", mlir::acc::DeviceType::Multicore)
126
.CasesLower("nvidia", "acc_device_nvidia",
127
mlir::acc::DeviceType::Nvidia)
128
.CaseLower("radeon", mlir::acc::DeviceType::Radeon);
129
}
130
131
mlir::acc::GangArgType decodeGangType(OpenACCGangKind gk) {
132
switch (gk) {
133
case OpenACCGangKind::Num:
134
return mlir::acc::GangArgType::Num;
135
case OpenACCGangKind::Dim:
136
return mlir::acc::GangArgType::Dim;
137
case OpenACCGangKind::Static:
138
return mlir::acc::GangArgType::Static;
139
}
140
llvm_unreachable("unknown gang kind");
141
}
142
143
template <typename U = void,
144
typename = std::enable_if_t<isCombinedType<OpTy>, U>>
145
void applyToLoopOp(const OpenACCClause &c) {
146
mlir::OpBuilder::InsertionGuard guardCase(builder);
147
builder.setInsertionPoint(operation.loopOp);
148
OpenACCClauseCIREmitter<mlir::acc::LoopOp> loopEmitter{
149
operation.loopOp, cgf, builder, dirKind, dirLoc};
150
loopEmitter.lastDeviceTypeValues = lastDeviceTypeValues;
151
loopEmitter.Visit(&c);
152
}
153
154
template <typename U = void,
155
typename = std::enable_if_t<isCombinedType<OpTy>, U>>
156
void applyToComputeOp(const OpenACCClause &c) {
157
mlir::OpBuilder::InsertionGuard guardCase(builder);
158
builder.setInsertionPoint(operation.computeOp);
159
OpenACCClauseCIREmitter<typename OpTy::ComputeOpTy> computeEmitter{
160
operation.computeOp, cgf, builder, dirKind, dirLoc};
161
162
computeEmitter.lastDeviceTypeValues = lastDeviceTypeValues;
163
164
// Async handler uses the first data operand to figure out where to insert
165
// its information if it is present. This ensures that the new handler will
166
// correctly set the insertion point for async.
167
if (!dataOperands.empty())
168
computeEmitter.dataOperands.push_back(dataOperands.front());
169
computeEmitter.Visit(&c);
170
171
// Make sure all of the new data operands are kept track of here. The
172
// combined constructs always apply 'async' to only the compute component,
173
// so we need to collect these.
174
dataOperands.append(computeEmitter.dataOperands);
175
}
176
177
mlir::acc::DataClauseModifier
178
convertModifiers(OpenACCModifierKind modifiers) {
179
using namespace mlir::acc;
180
static_assert(static_cast<int>(OpenACCModifierKind::Zero) ==
181
static_cast<int>(DataClauseModifier::zero) &&
182
static_cast<int>(OpenACCModifierKind::Readonly) ==
183
static_cast<int>(DataClauseModifier::readonly) &&
184
static_cast<int>(OpenACCModifierKind::AlwaysIn) ==
185
static_cast<int>(DataClauseModifier::alwaysin) &&
186
static_cast<int>(OpenACCModifierKind::AlwaysOut) ==
187
static_cast<int>(DataClauseModifier::alwaysout) &&
188
static_cast<int>(OpenACCModifierKind::Capture) ==
189
static_cast<int>(DataClauseModifier::capture));
190
191
DataClauseModifier mlirModifiers{};
192
193
// The MLIR representation of this represents `always` as `alwaysin` +
194
// `alwaysout`. So do a small fixup here.
195
if (isOpenACCModifierBitSet(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>
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>
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.
267
mlir::ArrayAttr getAsyncOnlyAttr() {
268
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
269
mlir::acc::KernelsOp, mlir::acc::DataOp,
270
mlir::acc::UpdateOp>) {
271
return operation.getAsyncOnlyAttr();
272
} else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp,
273
mlir::acc::ExitDataOp>) {
274
if (!operation.getAsyncAttr())
275
return mlir::ArrayAttr{};
276
277
llvm::SmallVector<mlir::Attribute> devTysTemp;
278
devTysTemp.push_back(mlir::acc::DeviceTypeAttr::get(
279
builder.getContext(), mlir::acc::DeviceType::None));
280
return mlir::ArrayAttr::get(builder.getContext(), devTysTemp);
281
} else if constexpr (isCombinedType<OpTy>) {
282
return operation.computeOp.getAsyncOnlyAttr();
283
}
284
285
// Note: 'wait' has async as well, but it cannot have data clauses, so we
286
// don't have to handle them here.
287
288
llvm_unreachable("getting asyncOnly when clause not valid on operation?");
289
}
290
291
// Helper function that covers for the fact that we don't have this function
292
// on all operation types.
293
mlir::ArrayAttr getAsyncOperandsDeviceTypeAttr() {
294
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
295
mlir::acc::KernelsOp, mlir::acc::DataOp,
296
mlir::acc::UpdateOp>) {
297
return operation.getAsyncOperandsDeviceTypeAttr();
298
} else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp,
299
mlir::acc::ExitDataOp>) {
300
if (!operation.getAsyncOperand())
301
return mlir::ArrayAttr{};
302
303
llvm::SmallVector<mlir::Attribute> devTysTemp;
304
devTysTemp.push_back(mlir::acc::DeviceTypeAttr::get(
305
builder.getContext(), mlir::acc::DeviceType::None));
306
return mlir::ArrayAttr::get(builder.getContext(), devTysTemp);
307
} else if constexpr (isCombinedType<OpTy>) {
308
return operation.computeOp.getAsyncOperandsDeviceTypeAttr();
309
}
310
311
// Note: 'wait' has async as well, but it cannot have data clauses, so we
312
// don't have to handle them here.
313
314
llvm_unreachable(
315
"getting asyncOperandsDeviceType when clause not valid on operation?");
316
}
317
318
// Helper function that covers for the fact that we don't have this function
319
// on all operation types.
320
mlir::OperandRange getAsyncOperands() {
321
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
322
mlir::acc::KernelsOp, mlir::acc::DataOp,
323
mlir::acc::UpdateOp>)
324
return operation.getAsyncOperands();
325
else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp,
326
mlir::acc::ExitDataOp>)
327
return operation.getAsyncOperandMutable();
328
else if constexpr (isCombinedType<OpTy>)
329
return operation.computeOp.getAsyncOperands();
330
331
// Note: 'wait' has async as well, but it cannot have data clauses, so we
332
// don't have to handle them here.
333
334
llvm_unreachable(
335
"getting asyncOperandsDeviceType when clause not valid on operation?");
336
}
337
338
// The 'data' clauses all require that we add the 'async' values from the
339
// operation to them. We've collected the data operands along the way, so use
340
// that list to get the current 'async' values.
341
void updateDataOperandAsyncValues() {
342
if (!hasAsyncClause || dataOperands.empty())
343
return;
344
345
for (mlir::Operation *dataOp : dataOperands) {
346
llvm::TypeSwitch<mlir::Operation *, void>(dataOp)
347
.Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto op) {
348
op.setAsyncOnlyAttr(getAsyncOnlyAttr());
349
op.setAsyncOperandsDeviceTypeAttr(getAsyncOperandsDeviceTypeAttr());
350
op.getAsyncOperandsMutable().assign(getAsyncOperands());
351
})
352
.Default([&](mlir::Operation *) {
353
llvm_unreachable("Not a data operation?");
354
});
355
}
356
}
357
358
public:
359
OpenACCClauseCIREmitter(OpTy &operation, CIRGen::CIRGenFunction &cgf,
360
CIRGen::CIRGenBuilderTy &builder,
361
OpenACCDirectiveKind dirKind, SourceLocation dirLoc)
362
: operation(operation), cgf(cgf), builder(builder), dirKind(dirKind),
363
dirLoc(dirLoc) {}
364
365
void VisitClause(const OpenACCClause &clause) {
366
clauseNotImplemented(clause);
367
}
368
369
// The entry point for the CIR emitter. All users should use this rather than
370
// 'visitClauseList', as this also handles the things that have to happen
371
// 'after' the clauses are all visited.
372
void emitClauses(ArrayRef<const OpenACCClause *> clauses) {
373
this->VisitClauseList(clauses);
374
updateDataOperandAsyncValues();
375
}
376
377
void VisitDefaultClause(const OpenACCDefaultClause &clause) {
378
// This type-trait checks if 'op'(the first arg) is one of the mlir::acc
379
// operations listed in the rest of the arguments.
380
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
381
mlir::acc::KernelsOp, mlir::acc::DataOp>) {
382
switch (clause.getDefaultClauseKind()) {
383
case OpenACCDefaultClauseKind::None:
384
operation.setDefaultAttr(mlir::acc::ClauseDefaultValue::None);
385
break;
386
case OpenACCDefaultClauseKind::Present:
387
operation.setDefaultAttr(mlir::acc::ClauseDefaultValue::Present);
388
break;
389
case OpenACCDefaultClauseKind::Invalid:
390
break;
391
}
392
} else if constexpr (isCombinedType<OpTy>) {
393
applyToComputeOp(clause);
394
} else {
395
llvm_unreachable("Unknown construct kind in VisitDefaultClause");
396
}
397
}
398
399
void VisitDeviceTypeClause(const OpenACCDeviceTypeClause &clause) {
400
setLastDeviceTypeClause(clause);
401
402
if constexpr (isOneOfTypes<OpTy, mlir::acc::InitOp,
403
mlir::acc::ShutdownOp>) {
404
for (const DeviceTypeArgument &arg : clause.getArchitectures())
405
operation.addDeviceType(builder.getContext(),
406
decodeDeviceType(arg.getIdentifierInfo()));
407
} else if constexpr (isOneOfTypes<OpTy, mlir::acc::SetOp>) {
408
assert(!operation.getDeviceTypeAttr() && "already have device-type?");
409
assert(clause.getArchitectures().size() <= 1);
410
411
if (!clause.getArchitectures().empty())
412
operation.setDeviceType(
413
decodeDeviceType(clause.getArchitectures()[0].getIdentifierInfo()));
414
} else if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
415
mlir::acc::SerialOp, mlir::acc::KernelsOp,
416
mlir::acc::DataOp, mlir::acc::LoopOp,
417
mlir::acc::UpdateOp>) {
418
// Nothing to do here, these constructs don't have any IR for these, as
419
// they just modify the other clauses IR. So setting of
420
// `lastDeviceTypeValues` (done above) is all we need.
421
} else if constexpr (isCombinedType<OpTy>) {
422
// Nothing to do here either, combined constructs are just going to use
423
// 'lastDeviceTypeValues' to set the value for the child visitor.
424
} else {
425
// TODO: When we've implemented this for everything, switch this to an
426
// unreachable. routine construct remains.
427
return clauseNotImplemented(clause);
428
}
429
}
430
431
void VisitNumWorkersClause(const OpenACCNumWorkersClause &clause) {
432
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
433
mlir::acc::KernelsOp>) {
434
operation.addNumWorkersOperand(builder.getContext(),
435
emitIntExpr(clause.getIntExpr()),
436
lastDeviceTypeValues);
437
} else if constexpr (isCombinedType<OpTy>) {
438
applyToComputeOp(clause);
439
} else {
440
llvm_unreachable("Unknown construct kind in VisitNumGangsClause");
441
}
442
}
443
444
void VisitVectorLengthClause(const OpenACCVectorLengthClause &clause) {
445
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
446
mlir::acc::KernelsOp>) {
447
operation.addVectorLengthOperand(builder.getContext(),
448
emitIntExpr(clause.getIntExpr()),
449
lastDeviceTypeValues);
450
} else if constexpr (isCombinedType<OpTy>) {
451
applyToComputeOp(clause);
452
} else {
453
llvm_unreachable("Unknown construct kind in VisitVectorLengthClause");
454
}
455
}
456
457
void VisitAsyncClause(const OpenACCAsyncClause &clause) {
458
hasAsyncClause = true;
459
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
460
mlir::acc::KernelsOp, mlir::acc::DataOp,
461
mlir::acc::EnterDataOp, mlir::acc::ExitDataOp,
462
mlir::acc::UpdateOp>) {
463
if (!clause.hasIntExpr()) {
464
operation.addAsyncOnly(builder.getContext(), lastDeviceTypeValues);
465
} else {
466
467
mlir::Value intExpr;
468
{
469
// Async int exprs can be referenced by the data operands, which means
470
// that the int-exprs have to appear before them. IF there is a data
471
// operand already, set the insertion point to 'before' it.
472
mlir::OpBuilder::InsertionGuard guardCase(builder);
473
if (!dataOperands.empty())
474
builder.setInsertionPoint(dataOperands.front());
475
intExpr = emitIntExpr(clause.getIntExpr());
476
}
477
operation.addAsyncOperand(builder.getContext(), intExpr,
478
lastDeviceTypeValues);
479
}
480
} else if constexpr (isOneOfTypes<OpTy, mlir::acc::WaitOp>) {
481
// Wait doesn't have a device_type, so its handling here is slightly
482
// different.
483
if (!clause.hasIntExpr())
484
operation.setAsync(true);
485
else
486
operation.getAsyncOperandMutable().append(
487
emitIntExpr(clause.getIntExpr()));
488
} else if constexpr (isCombinedType<OpTy>) {
489
applyToComputeOp(clause);
490
} else {
491
// TODO: When we've implemented this for everything, switch this to an
492
// unreachable. Combined constructs remain. update construct remains.
493
return clauseNotImplemented(clause);
494
}
495
}
496
497
void VisitSelfClause(const OpenACCSelfClause &clause) {
498
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
499
mlir::acc::KernelsOp>) {
500
if (clause.isEmptySelfClause()) {
501
operation.setSelfAttr(true);
502
} else if (clause.isConditionExprClause()) {
503
assert(clause.hasConditionExpr());
504
operation.getSelfCondMutable().append(
505
createCondition(clause.getConditionExpr()));
506
} else {
507
llvm_unreachable("var-list version of self shouldn't get here");
508
}
509
} else if constexpr (isOneOfTypes<OpTy, mlir::acc::UpdateOp>) {
510
assert(!clause.isEmptySelfClause() && !clause.isConditionExprClause() &&
511
"var-list version of self required for update");
512
for (const Expr *var : clause.getVarList())
513
addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::UpdateHostOp>(
514
var, mlir::acc::DataClause::acc_update_self, {},
515
/*structured=*/false, /*implicit=*/false);
516
} else if constexpr (isCombinedType<OpTy>) {
517
applyToComputeOp(clause);
518
} else {
519
llvm_unreachable("Unknown construct kind in VisitSelfClause");
520
}
521
}
522
523
void VisitHostClause(const OpenACCHostClause &clause) {
524
if constexpr (isOneOfTypes<OpTy, mlir::acc::UpdateOp>) {
525
for (const Expr *var : clause.getVarList())
526
addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::UpdateHostOp>(
527
var, mlir::acc::DataClause::acc_update_host, {},
528
/*structured=*/false, /*implicit=*/false);
529
} else {
530
llvm_unreachable("Unknown construct kind in VisitHostClause");
531
}
532
}
533
534
void VisitDeviceClause(const OpenACCDeviceClause &clause) {
535
if constexpr (isOneOfTypes<OpTy, mlir::acc::UpdateOp>) {
536
for (const Expr *var : clause.getVarList())
537
addDataOperand<mlir::acc::UpdateDeviceOp>(
538
var, mlir::acc::DataClause::acc_update_device, {},
539
/*structured=*/false, /*implicit=*/false);
540
} else {
541
llvm_unreachable("Unknown construct kind in VisitDeviceClause");
542
}
543
}
544
545
void VisitIfClause(const OpenACCIfClause &clause) {
546
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
547
mlir::acc::KernelsOp, mlir::acc::InitOp,
548
mlir::acc::ShutdownOp, mlir::acc::SetOp,
549
mlir::acc::DataOp, mlir::acc::WaitOp,
550
mlir::acc::HostDataOp, mlir::acc::EnterDataOp,
551
mlir::acc::ExitDataOp, mlir::acc::UpdateOp>) {
552
operation.getIfCondMutable().append(
553
createCondition(clause.getConditionExpr()));
554
} else if constexpr (isCombinedType<OpTy>) {
555
applyToComputeOp(clause);
556
} else {
557
llvm_unreachable("Unknown construct kind in VisitIfClause");
558
}
559
}
560
561
void VisitIfPresentClause(const OpenACCIfPresentClause &clause) {
562
if constexpr (isOneOfTypes<OpTy, mlir::acc::HostDataOp,
563
mlir::acc::UpdateOp>) {
564
operation.setIfPresent(true);
565
} else {
566
llvm_unreachable("unknown construct kind in VisitIfPresentClause");
567
}
568
}
569
570
void VisitDeviceNumClause(const OpenACCDeviceNumClause &clause) {
571
if constexpr (isOneOfTypes<OpTy, mlir::acc::InitOp, mlir::acc::ShutdownOp,
572
mlir::acc::SetOp>) {
573
operation.getDeviceNumMutable().append(emitIntExpr(clause.getIntExpr()));
574
} else {
575
llvm_unreachable(
576
"init, shutdown, set, are only valid device_num constructs");
577
}
578
}
579
580
void VisitNumGangsClause(const OpenACCNumGangsClause &clause) {
581
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp,
582
mlir::acc::KernelsOp>) {
583
llvm::SmallVector<mlir::Value> values;
584
for (const Expr *E : clause.getIntExprs())
585
values.push_back(emitIntExpr(E));
586
587
operation.addNumGangsOperands(builder.getContext(), values,
588
lastDeviceTypeValues);
589
} else if constexpr (isCombinedType<OpTy>) {
590
applyToComputeOp(clause);
591
} else {
592
llvm_unreachable("Unknown construct kind in VisitNumGangsClause");
593
}
594
}
595
596
void VisitWaitClause(const OpenACCWaitClause &clause) {
597
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
598
mlir::acc::KernelsOp, mlir::acc::DataOp,
599
mlir::acc::EnterDataOp, mlir::acc::ExitDataOp,
600
mlir::acc::UpdateOp>) {
601
if (!clause.hasExprs()) {
602
operation.addWaitOnly(builder.getContext(), lastDeviceTypeValues);
603
} else {
604
llvm::SmallVector<mlir::Value> values;
605
if (clause.hasDevNumExpr())
606
values.push_back(emitIntExpr(clause.getDevNumExpr()));
607
for (const Expr *E : clause.getQueueIdExprs())
608
values.push_back(emitIntExpr(E));
609
operation.addWaitOperands(builder.getContext(), clause.hasDevNumExpr(),
610
values, lastDeviceTypeValues);
611
}
612
} else if constexpr (isCombinedType<OpTy>) {
613
applyToComputeOp(clause);
614
} else {
615
// TODO: When we've implemented this for everything, switch this to an
616
// unreachable. update construct remains.
617
return clauseNotImplemented(clause);
618
}
619
}
620
621
void VisitDefaultAsyncClause(const OpenACCDefaultAsyncClause &clause) {
622
if constexpr (isOneOfTypes<OpTy, mlir::acc::SetOp>) {
623
operation.getDefaultAsyncMutable().append(
624
emitIntExpr(clause.getIntExpr()));
625
} else {
626
llvm_unreachable("set, is only valid device_num constructs");
627
}
628
}
629
630
void VisitSeqClause(const OpenACCSeqClause &clause) {
631
if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
632
operation.addSeq(builder.getContext(), lastDeviceTypeValues);
633
} else if constexpr (isCombinedType<OpTy>) {
634
applyToLoopOp(clause);
635
} else {
636
// TODO: When we've implemented this for everything, switch this to an
637
// unreachable. Routine construct remains.
638
return clauseNotImplemented(clause);
639
}
640
}
641
642
void VisitAutoClause(const OpenACCAutoClause &clause) {
643
if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
644
operation.addAuto(builder.getContext(), lastDeviceTypeValues);
645
} else if constexpr (isCombinedType<OpTy>) {
646
applyToLoopOp(clause);
647
} else {
648
// TODO: When we've implemented this for everything, switch this to an
649
// unreachable. Routine, construct remains.
650
return clauseNotImplemented(clause);
651
}
652
}
653
654
void VisitIndependentClause(const OpenACCIndependentClause &clause) {
655
if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
656
operation.addIndependent(builder.getContext(), lastDeviceTypeValues);
657
} else if constexpr (isCombinedType<OpTy>) {
658
applyToLoopOp(clause);
659
} else {
660
// TODO: When we've implemented this for everything, switch this to an
661
// unreachable. Routine construct remains.
662
return clauseNotImplemented(clause);
663
}
664
}
665
666
void VisitCollapseClause(const OpenACCCollapseClause &clause) {
667
if constexpr (isOneOfTypes<OpTy, mlir::acc::LoopOp>) {
668
llvm::APInt value =
669
clause.getIntExpr()->EvaluateKnownConstInt(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
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
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
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
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
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
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
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
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
866
void VisitDeleteClause(const OpenACCDeleteClause &clause) {
867
if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {
868
for (const Expr *var : clause.getVarList())
869
addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::DeleteOp>(
870
var, mlir::acc::DataClause::acc_delete, {},
871
/*structured=*/false,
872
/*implicit=*/false);
873
} else {
874
llvm_unreachable("Unknown construct kind in VisitDeleteClause");
875
}
876
}
877
878
void VisitDetachClause(const OpenACCDetachClause &clause) {
879
if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {
880
for (const Expr *var : clause.getVarList())
881
addDataOperand<mlir::acc::GetDevicePtrOp, mlir::acc::DetachOp>(
882
var, mlir::acc::DataClause::acc_detach, {},
883
/*structured=*/false,
884
/*implicit=*/false);
885
} else {
886
llvm_unreachable("Unknown construct kind in VisitDetachClause");
887
}
888
}
889
890
void VisitFinalizeClause(const OpenACCFinalizeClause &clause) {
891
if constexpr (isOneOfTypes<OpTy, mlir::acc::ExitDataOp>) {
892
operation.setFinalize(true);
893
} else {
894
llvm_unreachable("Unknown construct kind in VisitFinalizeClause");
895
}
896
}
897
898
void VisitUseDeviceClause(const OpenACCUseDeviceClause &clause) {
899
if constexpr (isOneOfTypes<OpTy, mlir::acc::HostDataOp>) {
900
for (const Expr *var : clause.getVarList())
901
addDataOperand<mlir::acc::UseDeviceOp>(
902
var, mlir::acc::DataClause::acc_use_device, {}, /*structured=*/true,
903
/*implicit=*/false);
904
} else {
905
llvm_unreachable("Unknown construct kind in VisitUseDeviceClause");
906
}
907
}
908
909
void VisitDevicePtrClause(const OpenACCDevicePtrClause &clause) {
910
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
911
mlir::acc::KernelsOp, mlir::acc::DataOp>) {
912
for (const Expr *var : clause.getVarList())
913
addDataOperand<mlir::acc::DevicePtrOp>(
914
var, mlir::acc::DataClause::acc_deviceptr, {},
915
/*structured=*/true,
916
/*implicit=*/false);
917
} else if constexpr (isCombinedType<OpTy>) {
918
applyToComputeOp(clause);
919
} else {
920
// TODO: When we've implemented this for everything, switch this to an
921
// unreachable. declare remains.
922
return clauseNotImplemented(clause);
923
}
924
}
925
926
void VisitNoCreateClause(const OpenACCNoCreateClause &clause) {
927
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
928
mlir::acc::KernelsOp, mlir::acc::DataOp>) {
929
for (const Expr *var : clause.getVarList())
930
addDataOperand<mlir::acc::NoCreateOp, mlir::acc::DeleteOp>(
931
var, mlir::acc::DataClause::acc_no_create, {}, /*structured=*/true,
932
/*implicit=*/false);
933
} else if constexpr (isCombinedType<OpTy>) {
934
applyToComputeOp(clause);
935
} else {
936
llvm_unreachable("Unknown construct kind in VisitNoCreateClause");
937
}
938
}
939
940
void VisitPresentClause(const OpenACCPresentClause &clause) {
941
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
942
mlir::acc::KernelsOp, mlir::acc::DataOp>) {
943
for (const Expr *var : clause.getVarList())
944
addDataOperand<mlir::acc::PresentOp, mlir::acc::DeleteOp>(
945
var, mlir::acc::DataClause::acc_present, {}, /*structured=*/true,
946
/*implicit=*/false);
947
} else if constexpr (isCombinedType<OpTy>) {
948
applyToComputeOp(clause);
949
} else {
950
// TODO: When we've implemented this for everything, switch this to an
951
// unreachable. declare remains.
952
return clauseNotImplemented(clause);
953
}
954
}
955
956
void VisitAttachClause(const OpenACCAttachClause &clause) {
957
if constexpr (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
958
mlir::acc::KernelsOp, mlir::acc::DataOp>) {
959
for (const Expr *var : clause.getVarList())
960
addDataOperand<mlir::acc::AttachOp, mlir::acc::DetachOp>(
961
var, mlir::acc::DataClause::acc_attach, {}, /*structured=*/true,
962
/*implicit=*/false);
963
} else if constexpr (isOneOfTypes<OpTy, mlir::acc::EnterDataOp>) {
964
for (const Expr *var : clause.getVarList())
965
addDataOperand<mlir::acc::AttachOp>(
966
var, mlir::acc::DataClause::acc_attach, {},
967
/*structured=*/false, /*implicit=*/false);
968
} else if constexpr (isCombinedType<OpTy>) {
969
applyToComputeOp(clause);
970
} else {
971
llvm_unreachable("Unknown construct kind in VisitAttachClause");
972
}
973
}
974
};
975
976
template <typename OpTy>
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>
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)
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
1038