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