MLIR 24.0.0git
OpenACCCG.cpp
Go to the documentation of this file.
1//===- OpenACCCG.cpp - OpenACC codegen ops, attributes, and types ---------===//
2//
3// Part of the MLIR 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// Implementation for OpenACC codegen operations, attributes, and types.
10// These correspond to the definitions in OpenACCCG*.td tablegen files
11// and are kept in a separate file because they do not represent direct mappings
12// of OpenACC language constructs; they are intermediate representations used
13// when decomposing and lowering primary `acc` dialect operations.
14//
15//===----------------------------------------------------------------------===//
16
24#include "mlir/IR/Region.h"
28#include "llvm/ADT/DenseMap.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/SmallVector.h"
31
32using namespace mlir;
33using namespace acc;
34
35namespace {
36/// Generic helper for single-region OpenACC ops that execute their body once
37/// and then continue after the operation with their results (if any).
38static void
42 if (point.isParent()) {
43 regions.push_back(RegionSuccessor(&region));
44 return;
45 }
46 regions.push_back(RegionSuccessor(op));
47}
48
50 RegionSuccessor successor) {
51 return successor.isOperation() ? ValueRange(op->getResults()) : ValueRange();
52}
53
54/// Remove empty acc.kernel_environment operations. If the operation has wait
55/// operands, create a acc.wait operation to preserve synchronization.
56struct RemoveEmptyKernelEnvironment
57 : public OpRewritePattern<acc::KernelEnvironmentOp> {
58 using OpRewritePattern<acc::KernelEnvironmentOp>::OpRewritePattern;
59
60 LogicalResult matchAndRewrite(acc::KernelEnvironmentOp op,
61 PatternRewriter &rewriter) const override {
62 assert(op->getNumRegions() == 1 && "expected op to have one region");
63
64 Block &block = op.getRegion().front();
65 if (!block.empty())
66 return failure();
67
68 // Remove empty kernel environment.
69 // Preserve synchronization by creating acc.wait operation if needed.
70 if (!op.getWaitOperands().empty() || op.getWaitOnlyAttr())
71 rewriter.replaceOpWithNewOp<acc::WaitOp>(
72 op, op.getWaitOperands(), /*asyncOperand=*/Value(),
73 op.getWaitDevnum(), /*async=*/nullptr, /*ifCond=*/Value());
74 else
75 rewriter.eraseOp(op);
76
77 return success();
78 }
79};
80
81struct KernelEnvironmentDedupDataOperands
82 : public OpRewritePattern<acc::KernelEnvironmentOp> {
83 using OpRewritePattern<acc::KernelEnvironmentOp>::OpRewritePattern;
84
85 static bool isReductionDataOperand(Value operand) {
86 Operation *def = operand.getDefiningOp();
87 if (!def)
88 return false;
89 std::optional<DataClause> clause = getDataClause(def);
90 if (!clause || *clause != DataClause::acc_reduction)
91 return false;
92 assert(isa<CopyinOp>(def) && "reduction clause must be an acc.copyin");
93 return true;
94 }
95
96 LogicalResult matchAndRewrite(acc::KernelEnvironmentOp op,
97 PatternRewriter &rewriter) const override {
98 SmallVector<Value> operands(op.getDataClauseOperands());
99 const unsigned numOperands = operands.size();
100 if (numOperands < 2)
101 return failure();
102
103 SmallVector<Value> kept;
104 SmallVector<std::pair<Value, Value>> useRewrites;
105 SmallVector<SmallVector<Value>> boundsStorage;
106 boundsStorage.reserve(numOperands);
107 llvm::DenseMap<std::pair<Value, ArrayRef<Value>>, unsigned>
108 keptIndexByVarAndBounds;
109 for (Value operand : operands) {
110 Operation *def = operand.getDefiningOp();
111 // acc.map_info is a finalized mapping. Its order and semantics stand;
112 // matching only its variable and bounds would ignore flags and
113 // descriptors.
114 if (!def || isa<MapInfoOp>(def)) {
115 kept.push_back(operand);
116 continue;
117 }
118
119 Value var = getVar(def);
120 if (!var) {
121 kept.push_back(operand);
122 continue;
123 }
124
125 boundsStorage.push_back(getBounds(def));
126 auto [it, inserted] = keptIndexByVarAndBounds.try_emplace(
127 std::make_pair(var, ArrayRef<Value>(boundsStorage.back())),
128 kept.size());
129 if (inserted) {
130 kept.push_back(operand);
131 continue;
132 }
133
134 Value &survivor = kept[it->second];
135 // A reduction acc.copyin is that copyin plus the reduction. Keeping
136 // another clause would drop the reduction.
137 if (isReductionDataOperand(operand) && !isReductionDataOperand(survivor))
138 std::swap(survivor, operand);
139 useRewrites.emplace_back(operand, survivor);
140 }
141 if (kept.size() == numOperands)
142 return failure();
143
144 for (auto [from, to] : useRewrites) {
145 rewriter.replaceUsesWithIf(from, to, [&](OpOperand &use) {
146 return op->isProperAncestor(use.getOwner());
147 });
148 }
149 rewriter.modifyOpInPlace(
150 op, [&] { op.getDataClauseOperandsMutable().assign(kept); });
151 return success();
152 }
153};
154
155// Capture `hasStream` before erasing inputs because segment metadata is only
156// repaired at the end of the modification transaction.
157static void setComputeRegionInputOperandSegments(ComputeRegionOp op,
158 PatternRewriter &rewriter,
159 size_t numInput,
160 bool hasStream) {
161 const size_t numLaunch = op.getLaunchArgs().size();
162 op->setInherentAttr(
163 rewriter.getStringAttr(ComputeRegionOp::getOperandSegmentSizeAttr()),
164 rewriter.getDenseI32ArrayAttr({static_cast<int32_t>(numLaunch),
165 static_cast<int32_t>(numInput),
166 hasStream ? 1 : 0}));
167}
168
169struct ComputeRegionRemoveDuplicateArgs
170 : public OpRewritePattern<ComputeRegionOp> {
171 using Base::Base;
172
173 LogicalResult matchAndRewrite(ComputeRegionOp op,
174 PatternRewriter &rewriter) const override {
175 Block *body = op.getBody();
176 const size_t numLaunch = op.getLaunchArgs().size();
177 size_t numInput = op.getInputArgs().size();
178 assert(body->getNumArguments() == numLaunch + numInput &&
179 "region args mismatch");
180
181 bool hasDuplicate = false;
182 for (size_t j = 1; j < numInput && !hasDuplicate; ++j)
183 for (size_t i = 0; i < j; ++i)
184 if (op->getOperand(static_cast<unsigned>(numLaunch + i)) ==
185 op->getOperand(static_cast<unsigned>(numLaunch + j))) {
186 hasDuplicate = true;
187 break;
188 }
189 if (!hasDuplicate)
190 return failure();
191
192 const bool hasStream = static_cast<bool>(op.getStream());
193 rewriter.modifyOpInPlace(op, [&] {
194 while (true) {
195 bool merged = false;
196 for (size_t j = 1; j < numInput && !merged; ++j) {
197 for (size_t i = 0; i < j; ++i) {
198 if (op->getOperand(static_cast<unsigned>(numLaunch + i)) !=
199 op->getOperand(static_cast<unsigned>(numLaunch + j)))
200 continue;
201 unsigned keepIdx = static_cast<unsigned>(numLaunch + i);
202 unsigned dropIdx = static_cast<unsigned>(numLaunch + j);
203 rewriter.replaceAllUsesWith(body->getArgument(dropIdx),
204 body->getArgument(keepIdx));
205 body->eraseArgument(dropIdx);
206 op->eraseOperand(dropIdx);
207 --numInput;
208 merged = true;
209 break;
210 }
211 }
212 if (!merged)
213 break;
214 }
215 setComputeRegionInputOperandSegments(op, rewriter, numInput, hasStream);
216 });
217 return success();
218 }
219};
220
221struct ComputeRegionRemoveUnusedArgs
222 : public OpRewritePattern<ComputeRegionOp> {
223 using Base::Base;
224
225 LogicalResult matchAndRewrite(ComputeRegionOp op,
226 PatternRewriter &rewriter) const override {
227 Block *body = op.getBody();
228 const size_t numLaunch = op.getLaunchArgs().size();
229 size_t numInput = op.getInputArgs().size();
230 assert(body->getNumArguments() == numLaunch + numInput &&
231 "region args mismatch");
232
233 bool hasUnused = false;
234 for (size_t k = numLaunch; k < numLaunch + numInput; ++k)
235 if (body->getArgument(static_cast<unsigned>(k)).use_empty()) {
236 hasUnused = true;
237 break;
238 }
239 if (!hasUnused)
240 return failure();
241
242 const bool hasStream = static_cast<bool>(op.getStream());
243 rewriter.modifyOpInPlace(op, [&] {
244 for (size_t k = numLaunch; k < numLaunch + numInput;) {
245 if (!body->getArgument(static_cast<unsigned>(k)).use_empty()) {
246 ++k;
247 continue;
248 }
249 body->eraseArgument(static_cast<unsigned>(k));
250 op->eraseOperand(static_cast<unsigned>(k));
251 --numInput;
252 }
253 setComputeRegionInputOperandSegments(op, rewriter, numInput, hasStream);
254 });
255 return success();
256 }
257};
258
259template <typename EffectTy>
260static void addOperandEffect(
262 &effects,
263 const MutableOperandRange &operand) {
264 for (unsigned i = 0, e = operand.size(); i < e; ++i)
265 effects.emplace_back(EffectTy::get(), &operand[i]);
266}
267
268template <typename EffectTy>
269static void addResultEffect(
271 &effects,
272 Value result) {
273 effects.emplace_back(EffectTy::get(), mlir::cast<mlir::OpResult>(result));
274}
275
276static int64_t gpuProcessorIndex(gpu::Processor p) {
277 switch (p) {
278 case gpu::Processor::Sequential:
279 return 0;
280 case gpu::Processor::ThreadX:
281 return 1;
282 case gpu::Processor::ThreadY:
283 return 2;
284 case gpu::Processor::ThreadZ:
285 return 3;
286 case gpu::Processor::BlockX:
287 return 4;
288 case gpu::Processor::BlockY:
289 return 5;
290 case gpu::Processor::BlockZ:
291 return 6;
292 }
293 llvm_unreachable("unhandled gpu::Processor");
294}
295
296static gpu::Processor indexToGpuProcessor(int64_t idx) {
297 switch (idx) {
298 case 0:
299 return gpu::Processor::Sequential;
300 case 1:
301 return gpu::Processor::ThreadX;
302 case 2:
303 return gpu::Processor::ThreadY;
304 case 3:
305 return gpu::Processor::ThreadZ;
306 case 4:
307 return gpu::Processor::BlockX;
308 case 5:
309 return gpu::Processor::BlockY;
310 case 6:
311 return gpu::Processor::BlockZ;
312 default:
313 return gpu::Processor::Sequential;
314 }
315}
316
317static GPUParallelDimAttr intToParDim(MLIRContext *context, int64_t dimInt) {
318 return GPUParallelDimAttr::get(
319 context, IntegerAttr::get(IndexType::get(context), dimInt));
320}
321
322static GPUParallelDimAttr processorParDim(MLIRContext *context,
323 gpu::Processor proc) {
324 return GPUParallelDimAttr::get(
325 context,
326 IntegerAttr::get(IndexType::get(context), gpuProcessorIndex(proc)));
327}
328
329static ParseResult parseProcessorValue(AsmParser &parser,
330 GPUParallelDimAttr &dim) {
331 std::string keyword;
332 llvm::SMLoc loc = parser.getCurrentLocation();
333 if (failed(parser.parseKeywordOrString(&keyword)))
334 return failure();
335 auto maybeProcessor = gpu::symbolizeProcessor(keyword);
336 if (!maybeProcessor)
337 return parser.emitError(loc)
338 << "expected one of ::mlir::gpu::Processor enum names";
339 dim = intToParDim(parser.getContext(), gpuProcessorIndex(*maybeProcessor));
340 return success();
341}
342
343static void printProcessorValue(AsmPrinter &printer,
344 const GPUParallelDimAttr &attr) {
345 gpu::Processor processor = indexToGpuProcessor(attr.getValue().getInt());
346 printer << gpu::stringifyProcessor(processor);
347}
348
349static FailureOr<SmallVector<GPUParallelDimAttr>>
350parseGPUParallelDimList(AsmParser &parser) {
352 auto parseParDim = [&]() -> ParseResult {
353 GPUParallelDimAttr dim;
354 if (parseProcessorValue(parser, dim))
355 return failure();
356 parDims.push_back(dim);
357 return success();
358 };
360 "list of OpenACC GPU parallel dimensions"))
361 return failure();
362 return parDims;
363}
364
365static void printGPUParallelDimList(AsmPrinter &printer,
367 printer << "[";
368 llvm::interleaveComma(dims, printer, [&printer](const GPUParallelDimAttr &p) {
369 printProcessorValue(printer, p);
370 });
371 printer << "]";
372}
373} // namespace
374
375//===----------------------------------------------------------------------===//
376// KernelEnvironmentOp
377//===----------------------------------------------------------------------===//
378
379void KernelEnvironmentOp::getSuccessorRegions(
381 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
382 regions);
383}
384
385ValueRange KernelEnvironmentOp::getSuccessorInputs(RegionSuccessor successor) {
386 return getSingleRegionSuccessorInputs(getOperation(), successor);
387}
388
389void KernelEnvironmentOp::getCanonicalizationPatterns(
390 RewritePatternSet &results, MLIRContext *context) {
391 results.add<RemoveEmptyKernelEnvironment, KernelEnvironmentDedupDataOperands>(
392 context);
393}
394
395/// Extract async for `clauseDeviceType`. Returns true if a clause was found.
396template <typename ComputeConstructT>
397static bool
398extractAsyncClause(ComputeConstructT computeConstruct,
399 DeviceType clauseDeviceType, MLIRContext *context,
400 std::optional<Value> &asyncOperand, UnitAttr &asyncOnly) {
401 if (computeConstruct.hasAsyncOnly(clauseDeviceType)) {
402 asyncOnly = UnitAttr::get(context);
403 return true;
404 }
405 if (Value asyncValue = computeConstruct.getAsyncValue(clauseDeviceType)) {
406 asyncOperand = asyncValue;
407 return true;
408 }
409 return false;
410}
411
412/// Extract wait for `clauseDeviceType`. Returns true if a clause was found.
413template <typename ComputeConstructT>
414static bool extractWaitClause(ComputeConstructT computeConstruct,
415 DeviceType clauseDeviceType, MLIRContext *context,
416 std::optional<Value> &waitDevnum,
417 SmallVectorImpl<Value> &waitOperands,
418 UnitAttr &waitOnly) {
419 if (computeConstruct.hasWaitOnly(clauseDeviceType)) {
420 waitOnly = UnitAttr::get(context);
421 return true;
422 }
423 Value devnum = computeConstruct.getWaitDevnum(clauseDeviceType);
424 auto waitValues = computeConstruct.getWaitValues(clauseDeviceType);
425 if (!devnum && waitValues.empty())
426 return false;
427 if (devnum)
428 waitDevnum = devnum;
429 waitOperands.append(waitValues.begin(), waitValues.end());
430 return true;
431}
432
433template <typename ComputeConstructT>
435 ComputeConstructT computeConstruct, DeviceType deviceType,
436 std::optional<Value> &asyncOperand, UnitAttr &asyncOnly,
437 std::optional<Value> &waitDevnum, SmallVectorImpl<Value> &waitOperands,
438 UnitAttr &waitOnly) {
439 MLIRContext *context = computeConstruct->getContext();
440
441 // Prefer device_type-specific clauses, then default ones.
442 if (!extractAsyncClause(computeConstruct, deviceType, context, asyncOperand,
443 asyncOnly)) {
444 if (deviceType != DeviceType::None)
445 extractAsyncClause(computeConstruct, DeviceType::None, context,
446 asyncOperand, asyncOnly);
447 }
448
449 if (!extractWaitClause(computeConstruct, deviceType, context, waitDevnum,
450 waitOperands, waitOnly)) {
451 if (deviceType != DeviceType::None)
452 extractWaitClause(computeConstruct, DeviceType::None, context, waitDevnum,
453 waitOperands, waitOnly);
454 }
455}
456
457template <typename ComputeConstructT>
458KernelEnvironmentOp
459KernelEnvironmentOp::createAndPopulate(ComputeConstructT computeConstruct,
460 DeviceType deviceType,
461 OpBuilder &builder) {
462 std::optional<Value> asyncOperand;
463 UnitAttr asyncOnly = nullptr;
464 std::optional<Value> waitDevnum;
465 SmallVector<Value> waitOperands;
466 UnitAttr waitOnly = nullptr;
467 populateKernelEnvironmentAsyncWait(computeConstruct, deviceType, asyncOperand,
468 asyncOnly, waitDevnum, waitOperands,
469 waitOnly);
470
471 auto kernelEnvironment = KernelEnvironmentOp::create(
472 builder, computeConstruct->getLoc(),
473 computeConstruct.getDataClauseOperands(), asyncOperand.value_or(Value()),
474 asyncOnly, waitDevnum.value_or(Value()), waitOperands, waitOnly);
475 Block &block = kernelEnvironment.getRegion().emplaceBlock();
476 builder.setInsertionPointToStart(&block);
477 return kernelEnvironment;
478}
479
480template KernelEnvironmentOp
481KernelEnvironmentOp::createAndPopulate<ParallelOp>(ParallelOp, DeviceType,
482 OpBuilder &);
483template KernelEnvironmentOp
484KernelEnvironmentOp::createAndPopulate<KernelsOp>(KernelsOp, DeviceType,
485 OpBuilder &);
486template KernelEnvironmentOp
487KernelEnvironmentOp::createAndPopulate<SerialOp>(SerialOp, DeviceType,
488 OpBuilder &);
489
490LogicalResult KernelEnvironmentOp::verify() {
491 if (getAsyncOnly() && getAsyncOperand())
492 return emitError("async-only cannot appear with async operand");
493 if (getWaitOnly() && (!getWaitOperands().empty() || getWaitDevnum()))
494 return emitError("wait-only cannot appear with wait operands or devnum");
495 if ((*this)->getParentOfType<KernelEnvironmentOp>())
496 return emitError(
497 "must not be nested within another acc.kernel_environment");
498 return success();
499}
500
501//===----------------------------------------------------------------------===//
502// FirstprivateMapInitialOp
503//===----------------------------------------------------------------------===//
504
505LogicalResult FirstprivateMapInitialOp::verify() {
506 if (getDataClause() != acc::DataClause::acc_firstprivate)
507 return emitError("data clause associated with firstprivate operation must "
508 "match its intent");
509 if (!getVar())
510 return emitError("must have var operand");
511 if (!mlir::isa<mlir::acc::PointerLikeType>(getVar().getType()) &&
512 !mlir::isa<mlir::acc::MappableType>(getVar().getType()))
513 return emitError("var must be mappable or pointer-like");
514 if (mlir::isa<mlir::acc::PointerLikeType>(getVar().getType()) &&
515 getVarType() == getVar().getType())
516 return emitError("varType must capture the element type of var");
517 if (getModifiers() != acc::DataClauseModifier::none)
518 return emitError("no data clause modifiers are allowed");
519 return success();
520}
521
522void FirstprivateMapInitialOp::getEffects(
524 &effects) {
525 effects.emplace_back(MemoryEffects::Read::get(),
527 addOperandEffect<MemoryEffects::Read>(effects, getVarMutable());
529}
530
531//===----------------------------------------------------------------------===//
532// ReductionInitOp
533//===----------------------------------------------------------------------===//
534
535void ReductionInitOp::getSuccessorRegions(
537 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
538 regions);
539}
540
541void ReductionInitOp::getRegionInvocationBounds(
542 ArrayRef<Attribute> operands,
543 SmallVectorImpl<InvocationBounds> &invocationBounds) {
544 invocationBounds.emplace_back(1, 1);
545}
546
547ValueRange ReductionInitOp::getSuccessorInputs(RegionSuccessor successor) {
548 return getSingleRegionSuccessorInputs(getOperation(), successor);
549}
550
551LogicalResult ReductionInitOp::verify() {
552 Block &block = getRegion().front();
553 if (auto yieldOp = dyn_cast<acc::YieldOp>(block.getTerminator())) {
554 if (yieldOp.getNumOperands() != 1)
555 return emitOpError(
556 "region must yield exactly one value (private storage)");
557 if (yieldOp.getOperand(0).getType() != getVar().getType())
558 return emitOpError("yielded value type must match var type");
559 }
560 return success();
561}
562
563//===----------------------------------------------------------------------===//
564// ReductionCombineRegionOp
565//===----------------------------------------------------------------------===//
566
567void ReductionCombineRegionOp::getSuccessorRegions(
569 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
570 regions);
571}
572
573void ReductionCombineRegionOp::getRegionInvocationBounds(
574 ArrayRef<Attribute> operands,
575 SmallVectorImpl<InvocationBounds> &invocationBounds) {
576 invocationBounds.emplace_back(1, 1);
577}
578
580ReductionCombineRegionOp::getSuccessorInputs(RegionSuccessor successor) {
581 return getSingleRegionSuccessorInputs(getOperation(), successor);
582}
583
584LogicalResult ReductionCombineRegionOp::verify() {
585 Block &block = getRegion().front();
586 if (auto yieldOp = dyn_cast<acc::YieldOp>(block.getTerminator())) {
587 if (yieldOp.getNumOperands() != 0)
588 return emitOpError("region must be terminated by acc.yield with no "
589 "operands");
590 }
591 return success();
592}
593
594//===----------------------------------------------------------------------===//
595// ReductionAccumulateOp
596//===----------------------------------------------------------------------===//
597
598LogicalResult ReductionAccumulateOp::verify() {
599 Type valueType = getValue().getType();
600 auto ptrLikeTy = cast<PointerLikeType>(getMemref().getType());
601 Type elementType = ptrLikeTy.getElementType();
602 if (!elementType)
603 return emitOpError("pointer-like destination must have an element type");
604 if (elementType != valueType)
605 return emitOpError("pointer-like element type must match value type");
606 if (getParDims().getArray().empty())
607 return emitOpError("par_dims must specify at least one parallel dimension");
608 return success();
609}
610
611//===----------------------------------------------------------------------===//
612// ReductionAccumulateArrayOp
613//===----------------------------------------------------------------------===//
614
615LogicalResult ReductionAccumulateArrayOp::verify() {
616 if (getParDims().getArray().empty())
617 return emitOpError("par_dims must specify at least one parallel dimension");
618 return success();
619}
620
621//===----------------------------------------------------------------------===//
622// ReductionCombineOp
623//===----------------------------------------------------------------------===//
624
625void ReductionCombineOp::getEffects(
627 &effects) {
628 effects.emplace_back(MemoryEffects::Read::get(), &getSrcMemrefMutable(),
630 effects.emplace_back(MemoryEffects::Read::get(), &getDestMemrefMutable(),
632 effects.emplace_back(MemoryEffects::Write::get(), &getDestMemrefMutable(),
634}
635
636//===----------------------------------------------------------------------===//
637// ComputeRegionOp
638//===----------------------------------------------------------------------===//
639
640static ParWidthOp getParWidthOpForLaunchArg(ComputeRegionOp op,
641 GPUParallelDimAttr parDim) {
642 for (auto launchArg : op.getLaunchArgs()) {
643 auto parOp = launchArg.getDefiningOp<ParWidthOp>();
644 if (!parOp)
645 continue;
646 auto launchArgDim = cast<GPUParallelDimAttr>(parOp.getParDim());
647 if (launchArgDim == parDim)
648 return parOp;
649 }
650 return nullptr;
651}
652
653std::optional<Value> ComputeRegionOp::getLaunchArg(GPUParallelDimAttr parDim) {
654 if (auto parWidthOp = getParWidthOpForLaunchArg(*this, parDim))
655 return parWidthOp.getResult();
656 return {};
657}
658
659std::optional<Value>
660ComputeRegionOp::getKnownLaunchArg(GPUParallelDimAttr parDim) {
661 if (auto parWidthOp = getParWidthOpForLaunchArg(*this, parDim))
662 if (parWidthOp.getLaunchArg())
663 return parWidthOp.getLaunchArg();
664 return {};
665}
666
667std::optional<uint64_t>
668ComputeRegionOp::getKnownConstantLaunchArg(GPUParallelDimAttr parDim) {
669 auto knownParWidth = getKnownLaunchArg(parDim);
670 if (knownParWidth.has_value())
671 return getConstantIntValue(knownParWidth.value());
672 return {};
673}
674
675BlockArgument ComputeRegionOp::appendInputArg(Value value) {
676 getInputArgsMutable().append(value);
677 return getBody()->addArgument(value.getType(), getLoc());
678}
679
680std::optional<BlockArgument>
681ComputeRegionOp::wireHoistedValueThroughIns(Value value) {
682 Region &region = getRegion();
683
684 auto useIsInRegion = [&](OpOperand &use) -> bool {
685 return region.isAncestor(use.getOwner()->getParentRegion());
686 };
687
688 if (!areValuesDefinedAbove(ValueRange(value), region) ||
689 !llvm::any_of(value.getUses(), useIsInRegion))
690 return std::nullopt;
691
692 BlockArgument arg = appendInputArg(value);
693 replaceAllUsesInRegionWith(value, arg, region);
694 return arg;
695}
696
697bool ComputeRegionOp::isEffectivelySerial() {
698 auto *ctx = getContext();
699
700 if (getLaunchArg(GPUParallelDimAttr::seqDim(ctx)))
701 return true;
702
703 auto checkDim = [&](GPUParallelDimAttr dim) -> bool {
704 auto val = getKnownConstantLaunchArg(dim);
705 return val && *val == 1;
706 };
707
708 return checkDim(GPUParallelDimAttr::threadXDim(ctx)) &&
709 checkDim(GPUParallelDimAttr::threadYDim(ctx)) &&
710 checkDim(GPUParallelDimAttr::threadZDim(ctx)) &&
711 checkDim(GPUParallelDimAttr::blockXDim(ctx)) &&
712 checkDim(GPUParallelDimAttr::blockYDim(ctx)) &&
713 checkDim(GPUParallelDimAttr::blockZDim(ctx));
714}
715
716BlockArgument ComputeRegionOp::parDimToWidth(GPUParallelDimAttr parDim) {
717 for (auto [pos, launchArg] : llvm::enumerate(getLaunchArgs())) {
718 auto parOp = launchArg.getDefiningOp<ParWidthOp>();
719 assert(parOp);
720 auto launchArgDim = cast<GPUParallelDimAttr>(parOp.getParDim());
721 if (launchArgDim == parDim) {
722 assert(pos < getRegion().front().getNumArguments() &&
723 "launch arg position out of range");
724 return getRegion().front().getArgument(pos);
725 }
726 }
727 llvm_unreachable("attempting to get unspecified parDim");
728}
729
730SmallVector<GPUParallelDimAttr> ComputeRegionOp::getLaunchParDims() {
732 for (auto launchArg : getLaunchArgs()) {
733 auto parOp = launchArg.getDefiningOp<ParWidthOp>();
734 auto launchArgDim = cast<GPUParallelDimAttr>(parOp.getParDim());
735 int64_t dimInt = launchArgDim.getValue().getInt();
736 parDims.push_back(intToParDim(getContext(), dimInt));
737 }
738 return parDims;
739}
740
741Value ComputeRegionOp::getOperand(BlockArgument blockArg) {
742 Block *body = getBody();
743 if (blockArg.getOwner() != body)
744 return Value();
745 unsigned argNumber = blockArg.getArgNumber();
746 unsigned numLaunchArgs = getLaunchArgs().size();
747 unsigned numInputArgs = getInputArgs().size();
748 if (argNumber >= numLaunchArgs + numInputArgs)
749 return Value();
750 if (argNumber < numLaunchArgs)
751 return getLaunchArgs()[argNumber];
752 return getInputArgs()[argNumber - numLaunchArgs];
753}
754
755std::optional<BlockArgument> ComputeRegionOp::getBlockArg(Value value) {
756 Block *body = getBody();
757 for (auto [idx, launchVal] : llvm::enumerate(getLaunchArgs())) {
758 if (launchVal == value)
759 return body->getArgument(idx);
760 }
761 unsigned numLaunch = getLaunchArgs().size();
762 for (auto [idx, inputVal] : llvm::enumerate(getInputArgs())) {
763 if (inputVal == value)
764 return body->getArgument(numLaunch + idx);
765 }
766 return std::nullopt;
767}
768
769void ComputeRegionOp::getCanonicalizationPatterns(RewritePatternSet &results,
770 MLIRContext *context) {
771 results.add<ComputeRegionRemoveDuplicateArgs, ComputeRegionRemoveUnusedArgs>(
772 context);
773}
774
775BlockArgument ComputeRegionOp::gpuParWidth(gpu::Processor processor) {
776 return parDimToWidth(GPUParallelDimAttr::get(getContext(), processor));
777}
778
779LogicalResult ComputeRegionOp::verify() {
780 for (auto op : getLaunchArgs())
781 if (!op.getDefiningOp<acc::ParWidthOp>())
782 return emitOpError(
783 "launch arguments must be results of acc.par_width operations");
784
785 unsigned expectedBlockArgs = getLaunchArgs().size() + getInputArgs().size();
786 unsigned actualBlockArgs = getRegion().front().getNumArguments();
787 if (expectedBlockArgs != actualBlockArgs)
788 return emitOpError("expected ")
789 << expectedBlockArgs << " block arguments (launch + input), got "
790 << actualBlockArgs;
791
792 return success();
793}
794
795void ComputeRegionOp::print(OpAsmPrinter &p) {
796 ValueRange regionArgs = getBody()->getArguments();
797 ValueRange launchArgs = getLaunchArgs();
798 ValueRange inputArgs = getInputArgs();
799
800 assert(regionArgs.size() == (launchArgs.size() + inputArgs.size()) &&
801 "region args mismatch");
802
803 if (getStream())
804 p << " stream(" << getStream() << " : " << getStream().getType() << ")";
805
806 size_t i = 0;
807 if (!launchArgs.empty()) {
808 p << " launch(";
809 for (size_t j = 0; j < launchArgs.size(); ++j, ++i) {
810 p << regionArgs[i] << " = " << launchArgs[j];
811 if (j < launchArgs.size() - 1)
812 p << ", ";
813 }
814 p << ")";
815 }
816 if (!inputArgs.empty()) {
817 p << " ins(";
818 for (size_t j = 0; j < inputArgs.size(); ++j, ++i) {
819 p << regionArgs[i] << " = " << inputArgs[j];
820 if (j < inputArgs.size() - 1)
821 p << ", ";
822 }
823 p << ") : (";
824 for (size_t j = 0; j < inputArgs.size(); ++j) {
825 p << inputArgs[j].getType();
826 if (j < inputArgs.size() - 1)
827 p << ", ";
828 }
829 p << ")";
830 }
831 p.printOptionalArrowTypeList(getResultTypes());
832 p << " ";
833 p.printRegion(getRegion(), /*printEntryBlockArgs=*/false);
834 ComputeRegionOp::printProperties(getContext(), p, getProperties(),
835 /*elidedProps=*/getOperandSegmentSizeAttr());
836 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
837}
838
839ParseResult ComputeRegionOp::parse(OpAsmParser &parser,
841 auto &builder = parser.getBuilder();
842
844 OpAsmParser::UnresolvedOperand streamOperand;
845 Type streamType;
848 SmallVector<Type> types;
849
850 bool hasStream = false;
851 if (succeeded(parser.parseOptionalKeyword("stream"))) {
852 hasStream = true;
853 if (parser.parseLParen() || parser.parseOperand(streamOperand) ||
854 parser.parseColon() || parser.parseType(streamType) ||
855 parser.parseRParen())
856 return failure();
857 }
858
859 if (succeeded(parser.parseOptionalKeyword("launch"))) {
860 if (parser.parseAssignmentList(regionArgs, launchOperands))
861 return failure();
862 Type indexType = builder.getIndexType();
863 for (size_t i = 0; i < regionArgs.size(); ++i)
864 types.push_back(indexType);
865 }
866
867 if (succeeded(parser.parseOptionalKeyword("ins"))) {
868 if (parser.parseAssignmentList(regionArgs, inputOperands) ||
869 parser.parseColon() || parser.parseLParen() ||
870 parser.parseTypeList(types) || parser.parseRParen())
871 return failure();
872 }
873
874 if (parser.parseOptionalArrowTypeList(result.types))
875 return failure();
876
877 for (auto [iterArg, type] : llvm::zip_equal(regionArgs, types))
878 iterArg.type = type;
879
880 Region *body = result.addRegion();
881 if (parser.parseRegion(*body, regionArgs))
882 return failure();
883 ComputeRegionOp::ensureTerminator(*body, parser.getBuilder(),
884 result.location);
885
886 const size_t numLaunchOperands = launchOperands.size();
887 const size_t numInputOperands = inputOperands.size();
888 assert(numLaunchOperands + numInputOperands == regionArgs.size() &&
889 "compute region args mismatch");
890
891 DenseI32ArrayAttr operandSegmentSizes = builder.getDenseI32ArrayAttr(
892 {static_cast<int32_t>(numLaunchOperands),
893 static_cast<int32_t>(numInputOperands), hasStream ? 1 : 0});
894
895 for (size_t i = 0; i < numLaunchOperands; ++i) {
896 if (parser.resolveOperand(launchOperands[i], types[i], result.operands))
897 return failure();
898 }
899
900 for (size_t i = numLaunchOperands; i < regionArgs.size(); ++i) {
901 if (parser.resolveOperand(inputOperands[i - numLaunchOperands], types[i],
902 result.operands))
903 return failure();
904 }
905
906 if (hasStream) {
907 if (parser.resolveOperand(streamOperand, streamType, result.operands))
908 return failure();
909 }
910
911 Attribute parsedProperties;
912 if (ComputeRegionOp::genericParseProperties(parser, parsedProperties))
913 return failure();
914 auto propertyDictionary = dyn_cast_or_null<DictionaryAttr>(parsedProperties);
915 if (parsedProperties && !propertyDictionary)
916 return parser.emitError(parser.getNameLoc(),
917 "expected properties dictionary");
918
919 NamedAttrList properties(propertyDictionary ? propertyDictionary
920 : builder.getDictionaryAttr({}));
921 properties.set(ComputeRegionOp::getOperandSegmentSizeAttr(),
922 operandSegmentSizes);
923 propertyDictionary = properties.getDictionary(builder.getContext());
924 auto emitError = [&]() {
925 return mlir::emitError(result.location, "invalid properties ")
926 << propertyDictionary << " for op " << result.name.getStringRef()
927 << ": ";
928 };
929 if (failed(ComputeRegionOp::setPropertiesFromParsedAttr(
930 result.getOrAddProperties<Properties>(), propertyDictionary,
931 emitError)))
932 return failure();
933
934 auto attrsLoc = parser.getCurrentLocation();
935 if (parser.parseOptionalAttrDict(result.attributes))
936 return failure();
937 for (StringRef attrName : ComputeRegionOp::getAttributeNames()) {
938 if (result.attributes.get(attrName))
939 return parser.emitError(attrsLoc)
940 << "inherent attribute '" << attrName
941 << "' cannot be parsed from attr-dict when strict properties in "
942 "assembly format is enabled";
943 }
944
945 return success();
946}
947
948//===----------------------------------------------------------------------===//
949// GPUSharedMemoryOp
950//===----------------------------------------------------------------------===//
951
952LogicalResult GPUSharedMemoryOp::verify() {
953 if (getNumCopies() <= 0)
954 return emitOpError("num_copies must be positive");
955 if (getStaticUpperBoundBytes() <= 0)
956 return emitOpError("static_upper_bound_bytes must be positive");
957
958 bool hasScaling = static_cast<bool>(getDynamicSharedMemoryScalingBytes());
959 bool hasFixed = static_cast<bool>(getDynamicSharedMemoryFixedBytes());
960 if (hasScaling != hasFixed)
961 return emitOpError(
962 "dynamic_shared_memory_scaling_bytes and "
963 "dynamic_shared_memory_fixed_bytes must both be present or both be "
964 "absent");
965 if (auto scalingAttr = getDynamicSharedMemoryScalingBytesAttr())
966 if (scalingAttr.getValue().isNegative())
967 return emitOpError("dynamic_shared_memory_scaling_bytes must be "
968 "non-negative");
969 if (auto fixedAttr = getDynamicSharedMemoryFixedBytesAttr())
970 if (fixedAttr.getValue().isNegative())
971 return emitOpError("dynamic_shared_memory_fixed_bytes must be "
972 "non-negative");
973
974 auto resultTy = cast<MemRefType>(getResult().getType());
975 auto addrSpace =
976 dyn_cast_if_present<gpu::AddressSpaceAttr>(resultTy.getMemorySpace());
977 if (!addrSpace || addrSpace.getValue() != gpu::AddressSpace::Workgroup)
978 return emitOpError("result memref must use #gpu.address_space<workgroup>");
979
980 return success();
981}
982
983//===----------------------------------------------------------------------===//
984// PredicateRegionOp
985//===----------------------------------------------------------------------===//
986
987LogicalResult PredicateRegionOp::verify() {
988 if (getRegion().empty())
989 return emitOpError("region needs to have at least one block");
990 if (getRegion().front().getNumArguments() > 0)
991 return emitOpError("region cannot have any arguments");
992 if (!getOperation()->getParentOfType<ComputeRegionOp>())
993 return emitOpError("must be nested within an acc.compute_region operation");
994 return success();
995}
996
997//===----------------------------------------------------------------------===//
998// MapInfoOp
999//===----------------------------------------------------------------------===//
1000
1001LogicalResult MapInfoOp::verify() {
1002 // A pointer-like var addresses the mapped object, so varType has to name that
1003 // object rather than the address of it.
1004 if (mlir::isa<acc::PointerLikeType>(getVar().getType()) &&
1005 getVarType() == getVar().getType())
1006 return emitOpError("varType must capture the element type of var");
1007
1008 // A descriptor operand is only meaningful together with the layout it
1009 // follows. Without a kind, consumers have no way to interpret it.
1010 if (getDesc() && getDescKind() == DataDescKind::none)
1011 return emitOpError("desc requires a descKind other than none");
1012
1013 // Bounds count elements of the mapped object, which is the OpenACC
1014 // descriptor layout, so it must be among the kinds named here.
1015 if (!getBounds().empty() &&
1016 !acc::bitEnumContainsAny(getDescKind(), DataDescKind::openacc))
1017 return emitOpError("bounds require descKind openacc");
1018
1019 // Anything below -1 has no meaning: -1 states that the size is unknown at
1020 // compile time and 0 defers it to bounds or to a descriptor.
1021 if (getSize()) {
1022 std::optional<int64_t> constantSize = getConstantIntValue(getSize());
1023 if (constantSize && *constantSize < -1)
1024 return emitOpError("size must be -1, 0, or a positive byte count");
1025 }
1026
1027 return success();
1028}
1029
1030//===----------------------------------------------------------------------===//
1031// GPUParallelDimAttr
1032//===----------------------------------------------------------------------===//
1033
1034GPUParallelDimAttr GPUParallelDimAttr::get(MLIRContext *context,
1035 gpu::Processor proc) {
1036 return processorParDim(context, proc);
1037}
1038
1039GPUParallelDimAttr GPUParallelDimAttr::seqDim(MLIRContext *context) {
1040 return processorParDim(context, gpu::Processor::Sequential);
1041}
1042
1043GPUParallelDimAttr GPUParallelDimAttr::threadXDim(MLIRContext *context) {
1044 return processorParDim(context, gpu::Processor::ThreadX);
1045}
1046
1047GPUParallelDimAttr GPUParallelDimAttr::threadYDim(MLIRContext *context) {
1048 return processorParDim(context, gpu::Processor::ThreadY);
1049}
1050
1051GPUParallelDimAttr GPUParallelDimAttr::threadZDim(MLIRContext *context) {
1052 return processorParDim(context, gpu::Processor::ThreadZ);
1053}
1054
1055GPUParallelDimAttr GPUParallelDimAttr::blockXDim(MLIRContext *context) {
1056 return processorParDim(context, gpu::Processor::BlockX);
1057}
1058
1059GPUParallelDimAttr GPUParallelDimAttr::blockYDim(MLIRContext *context) {
1060 return processorParDim(context, gpu::Processor::BlockY);
1061}
1062
1063GPUParallelDimAttr GPUParallelDimAttr::blockZDim(MLIRContext *context) {
1064 return processorParDim(context, gpu::Processor::BlockZ);
1065}
1066
1067Attribute GPUParallelDimAttr::parse(AsmParser &parser, Type type) {
1068 GPUParallelDimAttr dim;
1069 if (parser.parseLess() || parseProcessorValue(parser, dim) ||
1070 parser.parseGreater()) {
1071 parser.emitError(parser.getCurrentLocation(),
1072 "expected format `<` processor_name `>`");
1073 return {};
1074 }
1075 return dim;
1076}
1077
1078void GPUParallelDimAttr::print(AsmPrinter &printer) const {
1079 printer << "<";
1080 printProcessorValue(printer, *this);
1081 printer << ">";
1082}
1083
1084GPUParallelDimAttr GPUParallelDimAttr::threadDim(MLIRContext *context,
1085 unsigned index) {
1086 assert(index <= 2 && "thread dimension index must be 0, 1, or 2");
1087 switch (index) {
1088 case 0:
1089 return threadXDim(context);
1090 case 1:
1091 return threadYDim(context);
1092 case 2:
1093 return threadZDim(context);
1094 }
1095 llvm_unreachable("validated thread dimension index");
1096}
1097
1098GPUParallelDimAttr GPUParallelDimAttr::blockDim(MLIRContext *context,
1099 unsigned index) {
1100 assert(index <= 2 && "block dimension index must be 0, 1, or 2");
1101 switch (index) {
1102 case 0:
1103 return blockXDim(context);
1104 case 1:
1105 return blockYDim(context);
1106 case 2:
1107 return blockZDim(context);
1108 }
1109 llvm_unreachable("validated block dimension index");
1110}
1111
1112gpu::Processor GPUParallelDimAttr::getProcessor() const {
1113 return indexToGpuProcessor(getValue().getInt());
1114}
1115
1116int GPUParallelDimAttr::getOrder() const {
1117 return gpuProcessorIndex(getProcessor());
1118}
1119
1120GPUParallelDimAttr GPUParallelDimAttr::getOneHigher() const {
1121 int order = getOrder();
1122 if (order >= 6) // BlockZ is the highest
1123 return *this;
1124 return get(getContext(), indexToGpuProcessor(order + 1));
1125}
1126
1127GPUParallelDimAttr GPUParallelDimAttr::getOneLower() const {
1128 int order = getOrder();
1129 if (order <= 0) // Sequential is the lowest
1130 return *this;
1131 return get(getContext(), indexToGpuProcessor(order - 1));
1132}
1133
1134bool GPUParallelDimAttr::isSeq() const {
1135 return getProcessor() == gpu::Processor::Sequential;
1136}
1137bool GPUParallelDimAttr::isThreadX() const {
1138 return getProcessor() == gpu::Processor::ThreadX;
1139}
1140bool GPUParallelDimAttr::isThreadY() const {
1141 return getProcessor() == gpu::Processor::ThreadY;
1142}
1143bool GPUParallelDimAttr::isThreadZ() const {
1144 return getProcessor() == gpu::Processor::ThreadZ;
1145}
1146bool GPUParallelDimAttr::isBlockX() const {
1147 return getProcessor() == gpu::Processor::BlockX;
1148}
1149bool GPUParallelDimAttr::isBlockY() const {
1150 return getProcessor() == gpu::Processor::BlockY;
1151}
1152bool GPUParallelDimAttr::isBlockZ() const {
1153 return getProcessor() == gpu::Processor::BlockZ;
1154}
1155bool GPUParallelDimAttr::isAnyThread() const {
1156 return isThreadX() || isThreadY() || isThreadZ();
1157}
1158bool GPUParallelDimAttr::isAnyBlock() const {
1159 return isBlockX() || isBlockY() || isBlockZ();
1160}
1161
1162//===----------------------------------------------------------------------===//
1163// GPUParallelDimsAttr
1164//===----------------------------------------------------------------------===//
1165
1166GPUParallelDimsAttr GPUParallelDimsAttr::seq(MLIRContext *ctx) {
1167 return GPUParallelDimsAttr::get(ctx, {GPUParallelDimAttr::seqDim(ctx)});
1168}
1169
1170bool GPUParallelDimsAttr::isSeq() const {
1171 assert(!getArray().empty() && "no par_dims found");
1172 if (getArray().size() == 1) {
1173 auto parDim = dyn_cast<GPUParallelDimAttr>(getArray()[0]);
1174 assert(parDim && "expected GPUParallelDimAttr");
1175 return parDim.isSeq();
1176 }
1177 return false;
1178}
1179
1180bool GPUParallelDimsAttr::isParallel() const { return !isSeq(); }
1181
1182bool GPUParallelDimsAttr::isMultiDim() const { return getArray().size() > 1; }
1183
1184bool GPUParallelDimsAttr::hasAnyBlockLevel() const {
1185 return llvm::any_of(
1186 getArray(), [](const GPUParallelDimAttr &p) { return p.isAnyBlock(); });
1187}
1188
1189bool GPUParallelDimsAttr::hasOnlyBlockLevel() const {
1190 return !getArray().empty() &&
1191 llvm::all_of(getArray(), [](const GPUParallelDimAttr &p) {
1192 return p.isAnyBlock();
1193 });
1194}
1195
1196bool GPUParallelDimsAttr::hasOnlyThreadYLevel() const {
1197 return !getArray().empty() &&
1198 llvm::all_of(getArray(), [](const GPUParallelDimAttr &p) {
1199 return p.isThreadY();
1200 });
1201}
1202
1203bool GPUParallelDimsAttr::hasOnlyThreadXLevel() const {
1204 return !getArray().empty() &&
1205 llvm::all_of(getArray(), [](const GPUParallelDimAttr &p) {
1206 return p.isThreadX();
1207 });
1208}
1209
1210Attribute GPUParallelDimsAttr::parse(AsmParser &parser, Type type) {
1211 FailureOr<SmallVector<GPUParallelDimAttr>> parDims =
1212 parseGPUParallelDimList(parser);
1213 if (failed(parDims))
1214 return {};
1215 return GPUParallelDimsAttr::get(parser.getContext(), *parDims);
1216}
1217
1218void GPUParallelDimsAttr::print(AsmPrinter &printer) const {
1219 printGPUParallelDimList(printer, getArray());
1220}
1221
1222//===----------------------------------------------------------------------===//
1223// ActiveParDimsAttr
1224//===----------------------------------------------------------------------===//
1225
1226Attribute ActiveParDimsAttr::parse(AsmParser &parser, Type type) {
1227 FailureOr<SmallVector<GPUParallelDimAttr>> parDims =
1228 parseGPUParallelDimList(parser);
1229 if (failed(parDims))
1230 return {};
1231 return ActiveParDimsAttr::get(parser.getContext(), *parDims);
1232}
1233
1234void ActiveParDimsAttr::print(AsmPrinter &printer) const {
1235 printGPUParallelDimList(printer, getArray());
1236}
return success()
static void addOperandEffect(SmallVectorImpl< SideEffects::EffectInstance< MemoryEffects::Effect > > &effects, MutableOperandRange operand)
Helper to add an effect on an operand, referenced by its mutable range.
Definition OpenACC.cpp:1079
static void addResultEffect(SmallVectorImpl< SideEffects::EffectInstance< MemoryEffects::Effect > > &effects, Value result)
Helper to add an effect on a result value.
Definition OpenACC.cpp:1089
static void getSingleRegionOpSuccessorRegions(Operation *op, Region &region, RegionBranchPoint point, SmallVectorImpl< RegionSuccessor > &regions)
Generic helper for single-region OpenACC ops that execute their body once and then continue after the...
Definition OpenACC.cpp:157
static ValueRange getSingleRegionSuccessorInputs(Operation *op, RegionSuccessor successor)
Definition OpenACC.cpp:168
b getContext())
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static ParWidthOp getParWidthOpForLaunchArg(ComputeRegionOp op, GPUParallelDimAttr parDim)
static bool extractWaitClause(ComputeConstructT computeConstruct, DeviceType clauseDeviceType, MLIRContext *context, std::optional< Value > &waitDevnum, SmallVectorImpl< Value > &waitOperands, UnitAttr &waitOnly)
Extract wait for clauseDeviceType. Returns true if a clause was found.
static bool extractAsyncClause(ComputeConstructT computeConstruct, DeviceType clauseDeviceType, MLIRContext *context, std::optional< Value > &asyncOperand, UnitAttr &asyncOnly)
Extract async for clauseDeviceType. Returns true if a clause was found.
static void populateKernelEnvironmentAsyncWait(ComputeConstructT computeConstruct, DeviceType deviceType, std::optional< Value > &asyncOperand, UnitAttr &asyncOnly, std::optional< Value > &waitDevnum, SmallVectorImpl< Value > &waitOperands, UnitAttr &waitOnly)
This base class exposes generic asm parser hooks, usable across the various derived parsers.
@ Square
Square brackets surrounding zero or more operands.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult parseKeywordOrString(std::string *result)
Parse a keyword or a quoted string.
virtual ParseResult parseLess()=0
Parse a '<' token.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseColon()=0
Parse a : token.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
ParseResult parseTypeList(SmallVectorImpl< Type > &result)
Parse a type list.
This base class exposes generic asm printer hooks, usable across the various derived printers.
void printOptionalArrowTypeList(TypeRange &&types)
Print an optional arrow followed by a type list.
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class represents an argument of a Block.
Definition Value.h:306
unsigned getArgNumber() const
Returns the number of this argument.
Definition Value.h:318
Block * getOwner() const
Returns the block that owns this argument.
Definition Value.h:315
Block represents an ordered list of Operations.
Definition Block.h:34
bool empty()
Definition Block.h:173
BlockArgument getArgument(unsigned i)
Definition Block.h:154
unsigned getNumArguments()
Definition Block.h:153
Operation & front()
Definition Block.h:178
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
void eraseArgument(unsigned index)
Erase the argument at 'index' and remove it from the argument list.
Definition Block.cpp:198
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:171
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
MLIRContext * getContext() const
Definition Builders.h:56
IndexType getIndexType()
Definition Builders.cpp:59
DictionaryAttr getDictionaryAttr(ArrayRef< NamedAttribute > value)
Definition Builders.cpp:112
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
This class provides a mutable adaptor for a range of operands.
Definition ValueRange.h:119
unsigned size() const
Returns the current size of the range.
Definition ValueRange.h:157
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region &region, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
ParseResult parseAssignmentList(SmallVectorImpl< Argument > &lhs, SmallVectorImpl< UnresolvedOperand > &rhs)
Parse a list of assignments of the form (x1 = y1, x2 = y2, ...)
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
This class helps build Operations.
Definition Builders.h:210
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
This class represents an operand of an operation.
Definition Value.h:254
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
result_range getResults()
Definition Operation.h:440
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:247
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
bool isParent() const
Returns true if branching from the parent op.
This class represents a successor of a region.
bool isOperation() const
Return true if the successor is an operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
Definition Region.h:234
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
virtual void replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class represents a specific instance of an effect.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
bool use_empty() const
Returns true if this value has no uses.
Definition Value.h:208
Type getType() const
Return the type of this value.
Definition Value.h:105
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition Value.h:188
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
Operation * getOwner() const
Return the owner of this operand.
Definition UseDefLists.h:38
mlir::Value getAccVar(mlir::Operation *accDataClauseOp)
Used to obtain the accVar from a data clause operation.
Definition OpenACC.cpp:5072
mlir::Value getVar(mlir::Operation *accDataClauseOp)
Used to obtain the var from a data clause operation.
Definition OpenACC.cpp:5040
std::optional< mlir::acc::DataClause > getDataClause(mlir::Operation *accDataEntryOp)
Used to obtain the dataClause from a data entry operation.
Definition OpenACC.cpp:5159
mlir::SmallVector< mlir::Value > getBounds(mlir::Operation *accDataClauseOp)
Used to obtain bounds from an acc data clause operation.
Definition OpenACC.cpp:5091
Value getDesc(Operation *mapEntryOp)
Returns descriptor value from acc.map_info.
mlir::ArrayAttr getAsyncOnly(mlir::Operation *accDataClauseOp)
Returns an array of acc:DeviceTypeAttr attributes attached to an acc data clause operation,...
Definition OpenACC.cpp:5130
mlir::Type getVarType(mlir::Operation *accDataClauseOp)
Used to obtains the varType from a data clause operation which records the type of variable.
Definition OpenACC.cpp:5048
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
Include the generated interface declarations.
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region &region)
Replace all uses of orig within the given region with replacement.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:311
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
detail::DenseArrayAttrImpl< int32_t > DenseI32ArrayAttr
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
bool areValuesDefinedAbove(Range values, Region &limit)
Check if all values in the provided range are defined above the limit region.
Definition RegionUtils.h:26
This is the representation of an operand reference.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.