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/STLExtras.h"
29#include "llvm/ADT/SmallVector.h"
30
31using namespace mlir;
32using namespace acc;
33
34namespace {
35
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
81// Capture `hasStream` before erasing inputs because segment metadata is only
82// repaired at the end of the modification transaction.
83static void setComputeRegionInputOperandSegments(ComputeRegionOp op,
84 PatternRewriter &rewriter,
85 size_t numInput,
86 bool hasStream) {
87 const size_t numLaunch = op.getLaunchArgs().size();
88 op->setInherentAttr(
89 rewriter.getStringAttr(ComputeRegionOp::getOperandSegmentSizeAttr()),
90 rewriter.getDenseI32ArrayAttr({static_cast<int32_t>(numLaunch),
91 static_cast<int32_t>(numInput),
92 hasStream ? 1 : 0}));
93}
94
95struct ComputeRegionRemoveDuplicateArgs
96 : public OpRewritePattern<ComputeRegionOp> {
98
99 LogicalResult matchAndRewrite(ComputeRegionOp op,
100 PatternRewriter &rewriter) const override {
101 Block *body = op.getBody();
102 const size_t numLaunch = op.getLaunchArgs().size();
103 size_t numInput = op.getInputArgs().size();
104 assert(body->getNumArguments() == numLaunch + numInput &&
105 "region args mismatch");
106
107 bool hasDuplicate = false;
108 for (size_t j = 1; j < numInput && !hasDuplicate; ++j)
109 for (size_t i = 0; i < j; ++i)
110 if (op->getOperand(static_cast<unsigned>(numLaunch + i)) ==
111 op->getOperand(static_cast<unsigned>(numLaunch + j))) {
112 hasDuplicate = true;
113 break;
114 }
115 if (!hasDuplicate)
116 return failure();
117
118 const bool hasStream = static_cast<bool>(op.getStream());
119 rewriter.modifyOpInPlace(op, [&] {
120 while (true) {
121 bool merged = false;
122 for (size_t j = 1; j < numInput && !merged; ++j) {
123 for (size_t i = 0; i < j; ++i) {
124 if (op->getOperand(static_cast<unsigned>(numLaunch + i)) !=
125 op->getOperand(static_cast<unsigned>(numLaunch + j)))
126 continue;
127 unsigned keepIdx = static_cast<unsigned>(numLaunch + i);
128 unsigned dropIdx = static_cast<unsigned>(numLaunch + j);
129 rewriter.replaceAllUsesWith(body->getArgument(dropIdx),
130 body->getArgument(keepIdx));
131 body->eraseArgument(dropIdx);
132 op->eraseOperand(dropIdx);
133 --numInput;
134 merged = true;
135 break;
136 }
137 }
138 if (!merged)
139 break;
140 }
141 setComputeRegionInputOperandSegments(op, rewriter, numInput, hasStream);
142 });
143 return success();
144 }
145};
146
147struct ComputeRegionRemoveUnusedArgs
148 : public OpRewritePattern<ComputeRegionOp> {
150
151 LogicalResult matchAndRewrite(ComputeRegionOp op,
152 PatternRewriter &rewriter) const override {
153 Block *body = op.getBody();
154 const size_t numLaunch = op.getLaunchArgs().size();
155 size_t numInput = op.getInputArgs().size();
156 assert(body->getNumArguments() == numLaunch + numInput &&
157 "region args mismatch");
158
159 bool hasUnused = false;
160 for (size_t k = numLaunch; k < numLaunch + numInput; ++k)
161 if (body->getArgument(static_cast<unsigned>(k)).use_empty()) {
162 hasUnused = true;
163 break;
164 }
165 if (!hasUnused)
166 return failure();
167
168 const bool hasStream = static_cast<bool>(op.getStream());
169 rewriter.modifyOpInPlace(op, [&] {
170 for (size_t k = numLaunch; k < numLaunch + numInput;) {
171 if (!body->getArgument(static_cast<unsigned>(k)).use_empty()) {
172 ++k;
173 continue;
174 }
175 body->eraseArgument(static_cast<unsigned>(k));
176 op->eraseOperand(static_cast<unsigned>(k));
177 --numInput;
178 }
179 setComputeRegionInputOperandSegments(op, rewriter, numInput, hasStream);
180 });
181 return success();
182 }
183};
184
185template <typename EffectTy>
186static void addOperandEffect(
188 &effects,
189 const MutableOperandRange &operand) {
190 for (unsigned i = 0, e = operand.size(); i < e; ++i)
191 effects.emplace_back(EffectTy::get(), &operand[i]);
192}
193
194template <typename EffectTy>
195static void addResultEffect(
197 &effects,
198 Value result) {
199 effects.emplace_back(EffectTy::get(), mlir::cast<mlir::OpResult>(result));
200}
201
202static int64_t gpuProcessorIndex(gpu::Processor p) {
203 switch (p) {
204 case gpu::Processor::Sequential:
205 return 0;
206 case gpu::Processor::ThreadX:
207 return 1;
208 case gpu::Processor::ThreadY:
209 return 2;
210 case gpu::Processor::ThreadZ:
211 return 3;
212 case gpu::Processor::BlockX:
213 return 4;
214 case gpu::Processor::BlockY:
215 return 5;
216 case gpu::Processor::BlockZ:
217 return 6;
218 }
219 llvm_unreachable("unhandled gpu::Processor");
220}
221
222static gpu::Processor indexToGpuProcessor(int64_t idx) {
223 switch (idx) {
224 case 0:
225 return gpu::Processor::Sequential;
226 case 1:
227 return gpu::Processor::ThreadX;
228 case 2:
229 return gpu::Processor::ThreadY;
230 case 3:
231 return gpu::Processor::ThreadZ;
232 case 4:
233 return gpu::Processor::BlockX;
234 case 5:
235 return gpu::Processor::BlockY;
236 case 6:
237 return gpu::Processor::BlockZ;
238 default:
239 return gpu::Processor::Sequential;
240 }
241}
242
243static GPUParallelDimAttr intToParDim(MLIRContext *context, int64_t dimInt) {
244 return GPUParallelDimAttr::get(
245 context, IntegerAttr::get(IndexType::get(context), dimInt));
246}
247
248static GPUParallelDimAttr processorParDim(MLIRContext *context,
249 gpu::Processor proc) {
250 return GPUParallelDimAttr::get(
251 context,
252 IntegerAttr::get(IndexType::get(context), gpuProcessorIndex(proc)));
253}
254
255static ParseResult parseProcessorValue(AsmParser &parser,
256 GPUParallelDimAttr &dim) {
257 std::string keyword;
258 llvm::SMLoc loc = parser.getCurrentLocation();
259 if (failed(parser.parseKeywordOrString(&keyword)))
260 return failure();
261 auto maybeProcessor = gpu::symbolizeProcessor(keyword);
262 if (!maybeProcessor)
263 return parser.emitError(loc)
264 << "expected one of ::mlir::gpu::Processor enum names";
265 dim = intToParDim(parser.getContext(), gpuProcessorIndex(*maybeProcessor));
266 return success();
267}
268
269static void printProcessorValue(AsmPrinter &printer,
270 const GPUParallelDimAttr &attr) {
271 gpu::Processor processor = indexToGpuProcessor(attr.getValue().getInt());
272 printer << gpu::stringifyProcessor(processor);
273}
274
275static FailureOr<SmallVector<GPUParallelDimAttr>>
276parseGPUParallelDimList(AsmParser &parser) {
278 auto parseParDim = [&]() -> ParseResult {
279 GPUParallelDimAttr dim;
280 if (parseProcessorValue(parser, dim))
281 return failure();
282 parDims.push_back(dim);
283 return success();
284 };
286 "list of OpenACC GPU parallel dimensions"))
287 return failure();
288 return parDims;
289}
290
291static void printGPUParallelDimList(AsmPrinter &printer,
293 printer << "[";
294 llvm::interleaveComma(dims, printer, [&printer](const GPUParallelDimAttr &p) {
295 printProcessorValue(printer, p);
296 });
297 printer << "]";
298}
299
300} // namespace
301
302//===----------------------------------------------------------------------===//
303// KernelEnvironmentOp
304//===----------------------------------------------------------------------===//
305
306void KernelEnvironmentOp::getSuccessorRegions(
308 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
309 regions);
310}
311
312ValueRange KernelEnvironmentOp::getSuccessorInputs(RegionSuccessor successor) {
313 return getSingleRegionSuccessorInputs(getOperation(), successor);
314}
315
316void KernelEnvironmentOp::getCanonicalizationPatterns(
317 RewritePatternSet &results, MLIRContext *context) {
318 results.add<RemoveEmptyKernelEnvironment>(context);
319}
320
321/// Extract async for `clauseDeviceType`. Returns true if a clause was found.
322template <typename ComputeConstructT>
323static bool
324extractAsyncClause(ComputeConstructT computeConstruct,
325 DeviceType clauseDeviceType, MLIRContext *context,
326 std::optional<Value> &asyncOperand, UnitAttr &asyncOnly) {
327 if (computeConstruct.hasAsyncOnly(clauseDeviceType)) {
328 asyncOnly = UnitAttr::get(context);
329 return true;
330 }
331 if (Value asyncValue = computeConstruct.getAsyncValue(clauseDeviceType)) {
332 asyncOperand = asyncValue;
333 return true;
334 }
335 return false;
336}
337
338/// Extract wait for `clauseDeviceType`. Returns true if a clause was found.
339template <typename ComputeConstructT>
340static bool extractWaitClause(ComputeConstructT computeConstruct,
341 DeviceType clauseDeviceType, MLIRContext *context,
342 std::optional<Value> &waitDevnum,
343 SmallVectorImpl<Value> &waitOperands,
344 UnitAttr &waitOnly) {
345 if (computeConstruct.hasWaitOnly(clauseDeviceType)) {
346 waitOnly = UnitAttr::get(context);
347 return true;
348 }
349 Value devnum = computeConstruct.getWaitDevnum(clauseDeviceType);
350 auto waitValues = computeConstruct.getWaitValues(clauseDeviceType);
351 if (!devnum && waitValues.empty())
352 return false;
353 if (devnum)
354 waitDevnum = devnum;
355 waitOperands.append(waitValues.begin(), waitValues.end());
356 return true;
357}
358
359template <typename ComputeConstructT>
361 ComputeConstructT computeConstruct, DeviceType deviceType,
362 std::optional<Value> &asyncOperand, UnitAttr &asyncOnly,
363 std::optional<Value> &waitDevnum, SmallVectorImpl<Value> &waitOperands,
364 UnitAttr &waitOnly) {
365 MLIRContext *context = computeConstruct->getContext();
366
367 // Prefer device_type-specific clauses, then default ones.
368 if (!extractAsyncClause(computeConstruct, deviceType, context, asyncOperand,
369 asyncOnly)) {
370 if (deviceType != DeviceType::None)
371 extractAsyncClause(computeConstruct, DeviceType::None, context,
372 asyncOperand, asyncOnly);
373 }
374
375 if (!extractWaitClause(computeConstruct, deviceType, context, waitDevnum,
376 waitOperands, waitOnly)) {
377 if (deviceType != DeviceType::None)
378 extractWaitClause(computeConstruct, DeviceType::None, context, waitDevnum,
379 waitOperands, waitOnly);
380 }
381}
382
383template <typename ComputeConstructT>
384KernelEnvironmentOp
385KernelEnvironmentOp::createAndPopulate(ComputeConstructT computeConstruct,
386 DeviceType deviceType,
387 OpBuilder &builder) {
388 std::optional<Value> asyncOperand;
389 UnitAttr asyncOnly = nullptr;
390 std::optional<Value> waitDevnum;
391 SmallVector<Value> waitOperands;
392 UnitAttr waitOnly = nullptr;
393 populateKernelEnvironmentAsyncWait(computeConstruct, deviceType, asyncOperand,
394 asyncOnly, waitDevnum, waitOperands,
395 waitOnly);
396
397 auto kernelEnvironment = KernelEnvironmentOp::create(
398 builder, computeConstruct->getLoc(),
399 computeConstruct.getDataClauseOperands(), asyncOperand.value_or(Value()),
400 asyncOnly, waitDevnum.value_or(Value()), waitOperands, waitOnly);
401 Block &block = kernelEnvironment.getRegion().emplaceBlock();
402 builder.setInsertionPointToStart(&block);
403 return kernelEnvironment;
404}
405
406template KernelEnvironmentOp
407KernelEnvironmentOp::createAndPopulate<ParallelOp>(ParallelOp, DeviceType,
408 OpBuilder &);
409template KernelEnvironmentOp
410KernelEnvironmentOp::createAndPopulate<KernelsOp>(KernelsOp, DeviceType,
411 OpBuilder &);
412template KernelEnvironmentOp
413KernelEnvironmentOp::createAndPopulate<SerialOp>(SerialOp, DeviceType,
414 OpBuilder &);
415
416LogicalResult KernelEnvironmentOp::verify() {
417 if (getAsyncOnly() && getAsyncOperand())
418 return emitError("async-only cannot appear with async operand");
419 if (getWaitOnly() && (!getWaitOperands().empty() || getWaitDevnum()))
420 return emitError("wait-only cannot appear with wait operands or devnum");
421 return success();
422}
423
424//===----------------------------------------------------------------------===//
425// FirstprivateMapInitialOp
426//===----------------------------------------------------------------------===//
427
428LogicalResult FirstprivateMapInitialOp::verify() {
429 if (getDataClause() != acc::DataClause::acc_firstprivate)
430 return emitError("data clause associated with firstprivate operation must "
431 "match its intent");
432 if (!getVar())
433 return emitError("must have var operand");
434 if (!mlir::isa<mlir::acc::PointerLikeType>(getVar().getType()) &&
435 !mlir::isa<mlir::acc::MappableType>(getVar().getType()))
436 return emitError("var must be mappable or pointer-like");
437 if (mlir::isa<mlir::acc::PointerLikeType>(getVar().getType()) &&
438 getVarType() == getVar().getType())
439 return emitError("varType must capture the element type of var");
440 if (getModifiers() != acc::DataClauseModifier::none)
441 return emitError("no data clause modifiers are allowed");
442 return success();
443}
444
445void FirstprivateMapInitialOp::getEffects(
447 &effects) {
448 effects.emplace_back(MemoryEffects::Read::get(),
450 addOperandEffect<MemoryEffects::Read>(effects, getVarMutable());
452}
453
454//===----------------------------------------------------------------------===//
455// ReductionInitOp
456//===----------------------------------------------------------------------===//
457
458void ReductionInitOp::getSuccessorRegions(
460 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
461 regions);
462}
463
464void ReductionInitOp::getRegionInvocationBounds(
465 ArrayRef<Attribute> operands,
466 SmallVectorImpl<InvocationBounds> &invocationBounds) {
467 invocationBounds.emplace_back(1, 1);
468}
469
470ValueRange ReductionInitOp::getSuccessorInputs(RegionSuccessor successor) {
471 return getSingleRegionSuccessorInputs(getOperation(), successor);
472}
473
474LogicalResult ReductionInitOp::verify() {
475 Block &block = getRegion().front();
476 if (auto yieldOp = dyn_cast<acc::YieldOp>(block.getTerminator())) {
477 if (yieldOp.getNumOperands() != 1)
478 return emitOpError(
479 "region must yield exactly one value (private storage)");
480 if (yieldOp.getOperand(0).getType() != getVar().getType())
481 return emitOpError("yielded value type must match var type");
482 }
483 return success();
484}
485
486//===----------------------------------------------------------------------===//
487// ReductionCombineRegionOp
488//===----------------------------------------------------------------------===//
489
490void ReductionCombineRegionOp::getSuccessorRegions(
492 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
493 regions);
494}
495
496void ReductionCombineRegionOp::getRegionInvocationBounds(
497 ArrayRef<Attribute> operands,
498 SmallVectorImpl<InvocationBounds> &invocationBounds) {
499 invocationBounds.emplace_back(1, 1);
500}
501
503ReductionCombineRegionOp::getSuccessorInputs(RegionSuccessor successor) {
504 return getSingleRegionSuccessorInputs(getOperation(), successor);
505}
506
507LogicalResult ReductionCombineRegionOp::verify() {
508 Block &block = getRegion().front();
509 if (auto yieldOp = dyn_cast<acc::YieldOp>(block.getTerminator())) {
510 if (yieldOp.getNumOperands() != 0)
511 return emitOpError("region must be terminated by acc.yield with no "
512 "operands");
513 }
514 return success();
515}
516
517//===----------------------------------------------------------------------===//
518// ReductionAccumulateOp
519//===----------------------------------------------------------------------===//
520
521LogicalResult ReductionAccumulateOp::verify() {
522 Type valueType = getValue().getType();
523 auto ptrLikeTy = cast<PointerLikeType>(getMemref().getType());
524 Type elementType = ptrLikeTy.getElementType();
525 if (!elementType)
526 return emitOpError("pointer-like destination must have an element type");
527 if (elementType != valueType)
528 return emitOpError("pointer-like element type must match value type");
529 if (getParDims().getArray().empty())
530 return emitOpError("par_dims must specify at least one parallel dimension");
531 return success();
532}
533
534//===----------------------------------------------------------------------===//
535// ReductionAccumulateArrayOp
536//===----------------------------------------------------------------------===//
537
538LogicalResult ReductionAccumulateArrayOp::verify() {
539 if (getParDims().getArray().empty())
540 return emitOpError("par_dims must specify at least one parallel dimension");
541 return success();
542}
543
544//===----------------------------------------------------------------------===//
545// ReductionCombineOp
546//===----------------------------------------------------------------------===//
547
548void ReductionCombineOp::getEffects(
550 &effects) {
551 effects.emplace_back(MemoryEffects::Read::get(), &getSrcMemrefMutable(),
553 effects.emplace_back(MemoryEffects::Read::get(), &getDestMemrefMutable(),
555 effects.emplace_back(MemoryEffects::Write::get(), &getDestMemrefMutable(),
557}
558
559//===----------------------------------------------------------------------===//
560// ComputeRegionOp
561//===----------------------------------------------------------------------===//
562
563static ParWidthOp getParWidthOpForLaunchArg(ComputeRegionOp op,
564 GPUParallelDimAttr parDim) {
565 for (auto launchArg : op.getLaunchArgs()) {
566 auto parOp = launchArg.getDefiningOp<ParWidthOp>();
567 if (!parOp)
568 continue;
569 auto launchArgDim = cast<GPUParallelDimAttr>(parOp.getParDim());
570 if (launchArgDim == parDim)
571 return parOp;
572 }
573 return nullptr;
574}
575
576std::optional<Value> ComputeRegionOp::getLaunchArg(GPUParallelDimAttr parDim) {
577 if (auto parWidthOp = getParWidthOpForLaunchArg(*this, parDim))
578 return parWidthOp.getResult();
579 return {};
580}
581
582std::optional<Value>
583ComputeRegionOp::getKnownLaunchArg(GPUParallelDimAttr parDim) {
584 if (auto parWidthOp = getParWidthOpForLaunchArg(*this, parDim))
585 if (parWidthOp.getLaunchArg())
586 return parWidthOp.getLaunchArg();
587 return {};
588}
589
590std::optional<uint64_t>
591ComputeRegionOp::getKnownConstantLaunchArg(GPUParallelDimAttr parDim) {
592 auto knownParWidth = getKnownLaunchArg(parDim);
593 if (knownParWidth.has_value())
594 return getConstantIntValue(knownParWidth.value());
595 return {};
596}
597
598BlockArgument ComputeRegionOp::appendInputArg(Value value) {
599 getInputArgsMutable().append(value);
600 return getBody()->addArgument(value.getType(), getLoc());
601}
602
603std::optional<BlockArgument>
604ComputeRegionOp::wireHoistedValueThroughIns(Value value) {
605 Region &region = getRegion();
606
607 auto useIsInRegion = [&](OpOperand &use) -> bool {
608 return region.isAncestor(use.getOwner()->getParentRegion());
609 };
610
611 if (!areValuesDefinedAbove(ValueRange(value), region) ||
612 !llvm::any_of(value.getUses(), useIsInRegion))
613 return std::nullopt;
614
615 BlockArgument arg = appendInputArg(value);
616 replaceAllUsesInRegionWith(value, arg, region);
617 return arg;
618}
619
620bool ComputeRegionOp::isEffectivelySerial() {
621 auto *ctx = getContext();
622
623 if (getLaunchArg(GPUParallelDimAttr::seqDim(ctx)))
624 return true;
625
626 auto checkDim = [&](GPUParallelDimAttr dim) -> bool {
627 auto val = getKnownConstantLaunchArg(dim);
628 return val && *val == 1;
629 };
630
631 return checkDim(GPUParallelDimAttr::threadXDim(ctx)) &&
632 checkDim(GPUParallelDimAttr::threadYDim(ctx)) &&
633 checkDim(GPUParallelDimAttr::threadZDim(ctx)) &&
634 checkDim(GPUParallelDimAttr::blockXDim(ctx)) &&
635 checkDim(GPUParallelDimAttr::blockYDim(ctx)) &&
636 checkDim(GPUParallelDimAttr::blockZDim(ctx));
637}
638
639BlockArgument ComputeRegionOp::parDimToWidth(GPUParallelDimAttr parDim) {
640 for (auto [pos, launchArg] : llvm::enumerate(getLaunchArgs())) {
641 auto parOp = launchArg.getDefiningOp<ParWidthOp>();
642 assert(parOp);
643 auto launchArgDim = cast<GPUParallelDimAttr>(parOp.getParDim());
644 if (launchArgDim == parDim) {
645 assert(pos < getRegion().front().getNumArguments() &&
646 "launch arg position out of range");
647 return getRegion().front().getArgument(pos);
648 }
649 }
650 llvm_unreachable("attempting to get unspecified parDim");
651}
652
653SmallVector<GPUParallelDimAttr> ComputeRegionOp::getLaunchParDims() {
655 for (auto launchArg : getLaunchArgs()) {
656 auto parOp = launchArg.getDefiningOp<ParWidthOp>();
657 auto launchArgDim = cast<GPUParallelDimAttr>(parOp.getParDim());
658 int64_t dimInt = launchArgDim.getValue().getInt();
659 parDims.push_back(intToParDim(getContext(), dimInt));
660 }
661 return parDims;
662}
663
664Value ComputeRegionOp::getOperand(BlockArgument blockArg) {
665 Block *body = getBody();
666 if (blockArg.getOwner() != body)
667 return Value();
668 unsigned argNumber = blockArg.getArgNumber();
669 unsigned numLaunchArgs = getLaunchArgs().size();
670 unsigned numInputArgs = getInputArgs().size();
671 if (argNumber >= numLaunchArgs + numInputArgs)
672 return Value();
673 if (argNumber < numLaunchArgs)
674 return getLaunchArgs()[argNumber];
675 return getInputArgs()[argNumber - numLaunchArgs];
676}
677
678std::optional<BlockArgument> ComputeRegionOp::getBlockArg(Value value) {
679 Block *body = getBody();
680 for (auto [idx, launchVal] : llvm::enumerate(getLaunchArgs())) {
681 if (launchVal == value)
682 return body->getArgument(idx);
683 }
684 unsigned numLaunch = getLaunchArgs().size();
685 for (auto [idx, inputVal] : llvm::enumerate(getInputArgs())) {
686 if (inputVal == value)
687 return body->getArgument(numLaunch + idx);
688 }
689 return std::nullopt;
690}
691
692void ComputeRegionOp::getCanonicalizationPatterns(RewritePatternSet &results,
693 MLIRContext *context) {
694 results.add<ComputeRegionRemoveDuplicateArgs, ComputeRegionRemoveUnusedArgs>(
695 context);
696}
697
698BlockArgument ComputeRegionOp::gpuParWidth(gpu::Processor processor) {
699 return parDimToWidth(GPUParallelDimAttr::get(getContext(), processor));
700}
701
702LogicalResult ComputeRegionOp::verify() {
703 for (auto op : getLaunchArgs())
704 if (!op.getDefiningOp<acc::ParWidthOp>())
705 return emitOpError(
706 "launch arguments must be results of acc.par_width operations");
707
708 unsigned expectedBlockArgs = getLaunchArgs().size() + getInputArgs().size();
709 unsigned actualBlockArgs = getRegion().front().getNumArguments();
710 if (expectedBlockArgs != actualBlockArgs)
711 return emitOpError("expected ")
712 << expectedBlockArgs << " block arguments (launch + input), got "
713 << actualBlockArgs;
714
715 return success();
716}
717
718void ComputeRegionOp::print(OpAsmPrinter &p) {
719 ValueRange regionArgs = getBody()->getArguments();
720 ValueRange launchArgs = getLaunchArgs();
721 ValueRange inputArgs = getInputArgs();
722
723 assert(regionArgs.size() == (launchArgs.size() + inputArgs.size()) &&
724 "region args mismatch");
725
726 if (getStream())
727 p << " stream(" << getStream() << " : " << getStream().getType() << ")";
728
729 size_t i = 0;
730 if (!launchArgs.empty()) {
731 p << " launch(";
732 for (size_t j = 0; j < launchArgs.size(); ++j, ++i) {
733 p << regionArgs[i] << " = " << launchArgs[j];
734 if (j < launchArgs.size() - 1)
735 p << ", ";
736 }
737 p << ")";
738 }
739 if (!inputArgs.empty()) {
740 p << " ins(";
741 for (size_t j = 0; j < inputArgs.size(); ++j, ++i) {
742 p << regionArgs[i] << " = " << inputArgs[j];
743 if (j < inputArgs.size() - 1)
744 p << ", ";
745 }
746 p << ") : (";
747 for (size_t j = 0; j < inputArgs.size(); ++j) {
748 p << inputArgs[j].getType();
749 if (j < inputArgs.size() - 1)
750 p << ", ";
751 }
752 p << ")";
753 }
754 p.printOptionalArrowTypeList(getResultTypes());
755 p << " ";
756 p.printRegion(getRegion(), /*printEntryBlockArgs=*/false);
757 ComputeRegionOp::printProperties(getContext(), p, getProperties(),
758 /*elidedProps=*/getOperandSegmentSizeAttr());
759 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
760}
761
762ParseResult ComputeRegionOp::parse(OpAsmParser &parser,
764 auto &builder = parser.getBuilder();
765
767 OpAsmParser::UnresolvedOperand streamOperand;
768 Type streamType;
771 SmallVector<Type> types;
772
773 bool hasStream = false;
774 if (succeeded(parser.parseOptionalKeyword("stream"))) {
775 hasStream = true;
776 if (parser.parseLParen() || parser.parseOperand(streamOperand) ||
777 parser.parseColon() || parser.parseType(streamType) ||
778 parser.parseRParen())
779 return failure();
780 }
781
782 if (succeeded(parser.parseOptionalKeyword("launch"))) {
783 if (parser.parseAssignmentList(regionArgs, launchOperands))
784 return failure();
785 Type indexType = builder.getIndexType();
786 for (size_t i = 0; i < regionArgs.size(); ++i)
787 types.push_back(indexType);
788 }
789
790 if (succeeded(parser.parseOptionalKeyword("ins"))) {
791 if (parser.parseAssignmentList(regionArgs, inputOperands) ||
792 parser.parseColon() || parser.parseLParen() ||
793 parser.parseTypeList(types) || parser.parseRParen())
794 return failure();
795 }
796
797 if (parser.parseOptionalArrowTypeList(result.types))
798 return failure();
799
800 for (auto [iterArg, type] : llvm::zip_equal(regionArgs, types))
801 iterArg.type = type;
802
803 Region *body = result.addRegion();
804 if (parser.parseRegion(*body, regionArgs))
805 return failure();
806 ComputeRegionOp::ensureTerminator(*body, parser.getBuilder(),
807 result.location);
808
809 const size_t numLaunchOperands = launchOperands.size();
810 const size_t numInputOperands = inputOperands.size();
811 assert(numLaunchOperands + numInputOperands == regionArgs.size() &&
812 "compute region args mismatch");
813
814 DenseI32ArrayAttr operandSegmentSizes = builder.getDenseI32ArrayAttr(
815 {static_cast<int32_t>(numLaunchOperands),
816 static_cast<int32_t>(numInputOperands), hasStream ? 1 : 0});
817
818 for (size_t i = 0; i < numLaunchOperands; ++i) {
819 if (parser.resolveOperand(launchOperands[i], types[i], result.operands))
820 return failure();
821 }
822
823 for (size_t i = numLaunchOperands; i < regionArgs.size(); ++i) {
824 if (parser.resolveOperand(inputOperands[i - numLaunchOperands], types[i],
825 result.operands))
826 return failure();
827 }
828
829 if (hasStream) {
830 if (parser.resolveOperand(streamOperand, streamType, result.operands))
831 return failure();
832 }
833
834 Attribute parsedProperties;
835 if (ComputeRegionOp::genericParseProperties(parser, parsedProperties))
836 return failure();
837 auto propertyDictionary = dyn_cast_or_null<DictionaryAttr>(parsedProperties);
838 if (parsedProperties && !propertyDictionary)
839 return parser.emitError(parser.getNameLoc(),
840 "expected properties dictionary");
841
842 NamedAttrList properties(propertyDictionary ? propertyDictionary
843 : builder.getDictionaryAttr({}));
844 properties.set(ComputeRegionOp::getOperandSegmentSizeAttr(),
845 operandSegmentSizes);
846 propertyDictionary = properties.getDictionary(builder.getContext());
847 auto emitError = [&]() {
848 return mlir::emitError(result.location, "invalid properties ")
849 << propertyDictionary << " for op " << result.name.getStringRef()
850 << ": ";
851 };
852 if (failed(ComputeRegionOp::setPropertiesFromParsedAttr(
853 result.getOrAddProperties<Properties>(), propertyDictionary,
854 emitError)))
855 return failure();
856
857 auto attrsLoc = parser.getCurrentLocation();
858 if (parser.parseOptionalAttrDict(result.attributes))
859 return failure();
860 for (StringRef attrName : ComputeRegionOp::getAttributeNames()) {
861 if (result.attributes.get(attrName))
862 return parser.emitError(attrsLoc)
863 << "inherent attribute '" << attrName
864 << "' cannot be parsed from attr-dict when strict properties in "
865 "assembly format is enabled";
866 }
867
868 return success();
869}
870
871//===----------------------------------------------------------------------===//
872// GPUSharedMemoryOp
873//===----------------------------------------------------------------------===//
874
875LogicalResult GPUSharedMemoryOp::verify() {
876 if (getNumCopies() <= 0)
877 return emitOpError("num_copies must be positive");
878 if (getStaticUpperBoundBytes() <= 0)
879 return emitOpError("static_upper_bound_bytes must be positive");
880
881 bool hasScaling = static_cast<bool>(getDynamicSharedMemoryScalingBytes());
882 bool hasFixed = static_cast<bool>(getDynamicSharedMemoryFixedBytes());
883 if (hasScaling != hasFixed)
884 return emitOpError(
885 "dynamic_shared_memory_scaling_bytes and "
886 "dynamic_shared_memory_fixed_bytes must both be present or both be "
887 "absent");
888 if (auto scalingAttr = getDynamicSharedMemoryScalingBytesAttr())
889 if (scalingAttr.getValue().isNegative())
890 return emitOpError("dynamic_shared_memory_scaling_bytes must be "
891 "non-negative");
892 if (auto fixedAttr = getDynamicSharedMemoryFixedBytesAttr())
893 if (fixedAttr.getValue().isNegative())
894 return emitOpError("dynamic_shared_memory_fixed_bytes must be "
895 "non-negative");
896
897 auto resultTy = cast<MemRefType>(getResult().getType());
898 auto addrSpace =
899 dyn_cast_if_present<gpu::AddressSpaceAttr>(resultTy.getMemorySpace());
900 if (!addrSpace ||
901 addrSpace.getValue() != gpu::GPUDialect::getWorkgroupAddressSpace())
902 return emitOpError("result memref must use #gpu.address_space<workgroup>");
903
904 return success();
905}
906
907//===----------------------------------------------------------------------===//
908// PredicateRegionOp
909//===----------------------------------------------------------------------===//
910
911LogicalResult PredicateRegionOp::verify() {
912 if (getRegion().empty())
913 return emitOpError("region needs to have at least one block");
914 if (getRegion().front().getNumArguments() > 0)
915 return emitOpError("region cannot have any arguments");
916 if (!getOperation()->getParentOfType<ComputeRegionOp>())
917 return emitOpError("must be nested within an acc.compute_region operation");
918 return success();
919}
920
921//===----------------------------------------------------------------------===//
922// MapInfoOp
923//===----------------------------------------------------------------------===//
924
925LogicalResult MapInfoOp::verify() {
926 // A pointer-like var addresses the mapped object, so varType has to name that
927 // object rather than the address of it.
928 if (mlir::isa<acc::PointerLikeType>(getVar().getType()) &&
929 getVarType() == getVar().getType())
930 return emitOpError("varType must capture the element type of var");
931
932 // A descriptor operand is only meaningful together with the layout it
933 // follows. Without a kind, consumers have no way to interpret it.
934 if (getDesc() && getDescKind() == DataDescKind::none)
935 return emitOpError("desc requires a descKind other than none");
936
937 // Bounds count elements of the mapped object, which is the OpenACC
938 // descriptor layout, so it must be among the kinds named here.
939 if (!getBounds().empty() &&
940 !acc::bitEnumContainsAny(getDescKind(), DataDescKind::openacc))
941 return emitOpError("bounds require descKind openacc");
942
943 // Anything below -1 has no meaning: -1 states that the size is unknown at
944 // compile time and 0 defers it to bounds or to a descriptor.
945 if (getSize()) {
946 std::optional<int64_t> constantSize = getConstantIntValue(getSize());
947 if (constantSize && *constantSize < -1)
948 return emitOpError("size must be -1, 0, or a positive byte count");
949 }
950
951 return success();
952}
953
954//===----------------------------------------------------------------------===//
955// GPUParallelDimAttr
956//===----------------------------------------------------------------------===//
957
958GPUParallelDimAttr GPUParallelDimAttr::get(MLIRContext *context,
959 gpu::Processor proc) {
960 return processorParDim(context, proc);
961}
962
963GPUParallelDimAttr GPUParallelDimAttr::seqDim(MLIRContext *context) {
964 return processorParDim(context, gpu::Processor::Sequential);
965}
966
967GPUParallelDimAttr GPUParallelDimAttr::threadXDim(MLIRContext *context) {
968 return processorParDim(context, gpu::Processor::ThreadX);
969}
970
971GPUParallelDimAttr GPUParallelDimAttr::threadYDim(MLIRContext *context) {
972 return processorParDim(context, gpu::Processor::ThreadY);
973}
974
975GPUParallelDimAttr GPUParallelDimAttr::threadZDim(MLIRContext *context) {
976 return processorParDim(context, gpu::Processor::ThreadZ);
977}
978
979GPUParallelDimAttr GPUParallelDimAttr::blockXDim(MLIRContext *context) {
980 return processorParDim(context, gpu::Processor::BlockX);
981}
982
983GPUParallelDimAttr GPUParallelDimAttr::blockYDim(MLIRContext *context) {
984 return processorParDim(context, gpu::Processor::BlockY);
985}
986
987GPUParallelDimAttr GPUParallelDimAttr::blockZDim(MLIRContext *context) {
988 return processorParDim(context, gpu::Processor::BlockZ);
989}
990
991Attribute GPUParallelDimAttr::parse(AsmParser &parser, Type type) {
992 GPUParallelDimAttr dim;
993 if (parser.parseLess() || parseProcessorValue(parser, dim) ||
994 parser.parseGreater()) {
995 parser.emitError(parser.getCurrentLocation(),
996 "expected format `<` processor_name `>`");
997 return {};
998 }
999 return dim;
1000}
1001
1002void GPUParallelDimAttr::print(AsmPrinter &printer) const {
1003 printer << "<";
1004 printProcessorValue(printer, *this);
1005 printer << ">";
1006}
1007
1008GPUParallelDimAttr GPUParallelDimAttr::threadDim(MLIRContext *context,
1009 unsigned index) {
1010 assert(index <= 2 && "thread dimension index must be 0, 1, or 2");
1011 switch (index) {
1012 case 0:
1013 return threadXDim(context);
1014 case 1:
1015 return threadYDim(context);
1016 case 2:
1017 return threadZDim(context);
1018 }
1019 llvm_unreachable("validated thread dimension index");
1020}
1021
1022GPUParallelDimAttr GPUParallelDimAttr::blockDim(MLIRContext *context,
1023 unsigned index) {
1024 assert(index <= 2 && "block dimension index must be 0, 1, or 2");
1025 switch (index) {
1026 case 0:
1027 return blockXDim(context);
1028 case 1:
1029 return blockYDim(context);
1030 case 2:
1031 return blockZDim(context);
1032 }
1033 llvm_unreachable("validated block dimension index");
1034}
1035
1036gpu::Processor GPUParallelDimAttr::getProcessor() const {
1037 return indexToGpuProcessor(getValue().getInt());
1038}
1039
1040int GPUParallelDimAttr::getOrder() const {
1041 return gpuProcessorIndex(getProcessor());
1042}
1043
1044GPUParallelDimAttr GPUParallelDimAttr::getOneHigher() const {
1045 int order = getOrder();
1046 if (order >= 6) // BlockZ is the highest
1047 return *this;
1048 return get(getContext(), indexToGpuProcessor(order + 1));
1049}
1050
1051GPUParallelDimAttr GPUParallelDimAttr::getOneLower() const {
1052 int order = getOrder();
1053 if (order <= 0) // Sequential is the lowest
1054 return *this;
1055 return get(getContext(), indexToGpuProcessor(order - 1));
1056}
1057
1058bool GPUParallelDimAttr::isSeq() const {
1059 return getProcessor() == gpu::Processor::Sequential;
1060}
1061bool GPUParallelDimAttr::isThreadX() const {
1062 return getProcessor() == gpu::Processor::ThreadX;
1063}
1064bool GPUParallelDimAttr::isThreadY() const {
1065 return getProcessor() == gpu::Processor::ThreadY;
1066}
1067bool GPUParallelDimAttr::isThreadZ() const {
1068 return getProcessor() == gpu::Processor::ThreadZ;
1069}
1070bool GPUParallelDimAttr::isBlockX() const {
1071 return getProcessor() == gpu::Processor::BlockX;
1072}
1073bool GPUParallelDimAttr::isBlockY() const {
1074 return getProcessor() == gpu::Processor::BlockY;
1075}
1076bool GPUParallelDimAttr::isBlockZ() const {
1077 return getProcessor() == gpu::Processor::BlockZ;
1078}
1079bool GPUParallelDimAttr::isAnyThread() const {
1080 return isThreadX() || isThreadY() || isThreadZ();
1081}
1082bool GPUParallelDimAttr::isAnyBlock() const {
1083 return isBlockX() || isBlockY() || isBlockZ();
1084}
1085
1086//===----------------------------------------------------------------------===//
1087// GPUParallelDimsAttr
1088//===----------------------------------------------------------------------===//
1089
1090GPUParallelDimsAttr GPUParallelDimsAttr::seq(MLIRContext *ctx) {
1091 return GPUParallelDimsAttr::get(ctx, {GPUParallelDimAttr::seqDim(ctx)});
1092}
1093
1094bool GPUParallelDimsAttr::isSeq() const {
1095 assert(!getArray().empty() && "no par_dims found");
1096 if (getArray().size() == 1) {
1097 auto parDim = dyn_cast<GPUParallelDimAttr>(getArray()[0]);
1098 assert(parDim && "expected GPUParallelDimAttr");
1099 return parDim.isSeq();
1100 }
1101 return false;
1102}
1103
1104bool GPUParallelDimsAttr::isParallel() const { return !isSeq(); }
1105
1106bool GPUParallelDimsAttr::isMultiDim() const { return getArray().size() > 1; }
1107
1108bool GPUParallelDimsAttr::hasAnyBlockLevel() const {
1109 return llvm::any_of(
1110 getArray(), [](const GPUParallelDimAttr &p) { return p.isAnyBlock(); });
1111}
1112
1113bool GPUParallelDimsAttr::hasOnlyBlockLevel() const {
1114 return !getArray().empty() &&
1115 llvm::all_of(getArray(), [](const GPUParallelDimAttr &p) {
1116 return p.isAnyBlock();
1117 });
1118}
1119
1120bool GPUParallelDimsAttr::hasOnlyThreadYLevel() const {
1121 return !getArray().empty() &&
1122 llvm::all_of(getArray(), [](const GPUParallelDimAttr &p) {
1123 return p.isThreadY();
1124 });
1125}
1126
1127bool GPUParallelDimsAttr::hasOnlyThreadXLevel() const {
1128 return !getArray().empty() &&
1129 llvm::all_of(getArray(), [](const GPUParallelDimAttr &p) {
1130 return p.isThreadX();
1131 });
1132}
1133
1134Attribute GPUParallelDimsAttr::parse(AsmParser &parser, Type type) {
1135 FailureOr<SmallVector<GPUParallelDimAttr>> parDims =
1136 parseGPUParallelDimList(parser);
1137 if (failed(parDims))
1138 return {};
1139 return GPUParallelDimsAttr::get(parser.getContext(), *parDims);
1140}
1141
1142void GPUParallelDimsAttr::print(AsmPrinter &printer) const {
1143 printGPUParallelDimList(printer, getArray());
1144}
1145
1146//===----------------------------------------------------------------------===//
1147// ActiveParDimsAttr
1148//===----------------------------------------------------------------------===//
1149
1150Attribute ActiveParDimsAttr::parse(AsmParser &parser, Type type) {
1151 FailureOr<SmallVector<GPUParallelDimAttr>> parDims =
1152 parseGPUParallelDimList(parser);
1153 if (failed(parDims))
1154 return {};
1155 return ActiveParDimsAttr::get(parser.getContext(), *parDims);
1156}
1157
1158void ActiveParDimsAttr::print(AsmPrinter &printer) const {
1159 printGPUParallelDimList(printer, getArray());
1160}
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:1455
static void addResultEffect(SmallVectorImpl< SideEffects::EffectInstance< MemoryEffects::Effect > > &effects, Value result)
Helper to add an effect on a result value.
Definition OpenACC.cpp:1465
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:533
static ValueRange getSingleRegionSuccessorInputs(Operation *op, RegionSuccessor successor)
Definition OpenACC.cpp:544
b getContext())
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:63
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
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.
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
mlir::Value getAccVar(mlir::Operation *accDataClauseOp)
Used to obtain the accVar from a data clause operation.
Definition OpenACC.cpp:5400
mlir::Value getVar(mlir::Operation *accDataClauseOp)
Used to obtain the var from a data clause operation.
Definition OpenACC.cpp:5368
std::optional< mlir::acc::DataClause > getDataClause(mlir::Operation *accDataEntryOp)
Used to obtain the dataClause from a data entry operation.
Definition OpenACC.cpp:5477
mlir::SmallVector< mlir::Value > getBounds(mlir::Operation *accDataClauseOp)
Used to obtain bounds from an acc data clause operation.
Definition OpenACC.cpp:5419
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:5458
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:5376
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:733
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:310
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...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...
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.