19 #include "llvm/ADT/SmallSet.h"
20 #include "llvm/ADT/TypeSwitch.h"
25 #include "mlir/Dialect/OpenACC/OpenACCOpsDialect.cpp.inc"
26 #include "mlir/Dialect/OpenACC/OpenACCOpsEnums.cpp.inc"
27 #include "mlir/Dialect/OpenACC/OpenACCOpsInterfaces.cpp.inc"
28 #include "mlir/Dialect/OpenACC/OpenACCTypeInterfaces.cpp.inc"
29 #include "mlir/Dialect/OpenACCMPCommon/Interfaces/OpenACCMPOpsInterfaces.cpp.inc"
32 struct MemRefPointerLikeModel
33 :
public PointerLikeType::ExternalModel<MemRefPointerLikeModel,
36 return llvm::cast<MemRefType>(pointer).getElementType();
40 struct LLVMPointerPointerLikeModel
41 :
public PointerLikeType::ExternalModel<LLVMPointerPointerLikeModel,
42 LLVM::LLVMPointerType> {
51 void OpenACCDialect::initialize() {
54 #include "mlir/Dialect/OpenACC/OpenACCOps.cpp.inc"
57 #define GET_ATTRDEF_LIST
58 #include "mlir/Dialect/OpenACC/OpenACCOpsAttributes.cpp.inc"
61 #define GET_TYPEDEF_LIST
62 #include "mlir/Dialect/OpenACC/OpenACCOpsTypes.cpp.inc"
68 MemRefType::attachInterface<MemRefPointerLikeModel>(*
getContext());
69 LLVM::LLVMPointerType::attachInterface<LLVMPointerPointerLikeModel>(
78 if (arrayAttr && *arrayAttr && arrayAttr->size() > 0)
84 mlir::acc::DeviceType deviceType) {
88 for (
auto attr : *arrayAttr) {
89 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
90 if (deviceTypeAttr.getValue() == deviceType)
98 std::optional<mlir::ArrayAttr> deviceTypes) {
103 llvm::interleaveComma(*deviceTypes, p,
109 mlir::acc::DeviceType deviceType) {
110 unsigned segmentIdx = 0;
111 for (
auto attr : segments) {
112 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
113 if (deviceTypeAttr.getValue() == deviceType)
114 return std::make_optional(segmentIdx);
124 mlir::acc::DeviceType deviceType) {
126 return range.take_front(0);
127 if (
auto pos =
findSegment(*arrayAttr, deviceType)) {
128 int32_t nbOperandsBefore = 0;
129 for (
unsigned i = 0; i < *pos; ++i)
130 nbOperandsBefore += (*segments)[i];
131 return range.drop_front(nbOperandsBefore).take_front((*segments)[*pos]);
133 return range.take_front(0);
140 std::optional<mlir::ArrayAttr> hasWaitDevnum,
141 mlir::acc::DeviceType deviceType) {
144 if (
auto pos =
findSegment(*deviceTypeAttr, deviceType))
145 if (hasWaitDevnum->getValue()[*pos])
156 std::optional<mlir::ArrayAttr> hasWaitDevnum,
157 mlir::acc::DeviceType deviceType) {
162 if (
auto pos =
findSegment(*deviceTypeAttr, deviceType)) {
163 if (hasWaitDevnum && *hasWaitDevnum) {
164 auto boolAttr = mlir::dyn_cast<mlir::BoolAttr>((*hasWaitDevnum)[*pos]);
165 if (boolAttr.getValue())
166 return range.drop_front(1);
172 template <
typename Op>
174 for (uint32_t dtypeInt = 0; dtypeInt != acc::getMaxEnumValForDeviceType();
176 auto dtype =
static_cast<acc::DeviceType
>(dtypeInt);
181 op.hasAsyncOnly(dtype))
182 return op.
emitError(
"async attribute cannot appear with asyncOperand");
187 op.hasWaitOnly(dtype))
188 return op.
emitError(
"wait attribute cannot appear with waitOperands");
197 auto extent = getExtent();
198 auto upperbound = getUpperbound();
199 if (!extent && !upperbound)
200 return emitError(
"expected extent or upperbound.");
210 "data clause associated with private operation must match its intent");
219 return emitError(
"data clause associated with firstprivate operation must "
229 return emitError(
"data clause associated with reduction operation must "
239 return emitError(
"data clause associated with deviceptr operation must "
250 "data clause associated with present operation must match its intent");
259 if (!getImplicit() &&
getDataClause() != acc::DataClause::acc_copyin &&
264 "data clause associated with copyin operation must match its intent"
265 " or specify original clause this operation was decomposed from");
269 bool acc::CopyinOp::isCopyinReadonly() {
270 return getDataClause() == acc::DataClause::acc_copyin_readonly;
283 "data clause associated with create operation must match its intent"
284 " or specify original clause this operation was decomposed from");
288 bool acc::CreateOp::isCreateZero() {
290 return getDataClause() == acc::DataClause::acc_create_zero ||
299 return emitError(
"data clause associated with no_create operation must "
310 "data clause associated with attach operation must match its intent");
319 if (
getDataClause() != acc::DataClause::acc_declare_device_resident)
320 return emitError(
"data clause associated with device_resident operation "
321 "must match its intent");
332 "data clause associated with link operation must match its intent");
346 "data clause associated with copyout operation must match its intent"
347 " or specify original clause this operation was decomposed from");
349 return emitError(
"must have both host and device pointers");
353 bool acc::CopyoutOp::isCopyoutZero() {
368 getDataClause() != acc::DataClause::acc_declare_device_resident &&
371 "data clause associated with delete operation must match its intent"
372 " or specify original clause this operation was decomposed from");
374 return emitError(
"must have device pointer");
386 "data clause associated with detach operation must match its intent"
387 " or specify original clause this operation was decomposed from");
389 return emitError(
"must have device pointer");
401 "data clause associated with host operation must match its intent"
402 " or specify original clause this operation was decomposed from");
404 return emitError(
"must have both host and device pointers");
415 "data clause associated with device operation must match its intent"
416 " or specify original clause this operation was decomposed from");
427 "data clause associated with use_device operation must match its intent"
428 " or specify original clause this operation was decomposed from");
440 "data clause associated with cache operation must match its intent"
441 " or specify original clause this operation was decomposed from");
445 template <
typename StructureOp>
447 unsigned nRegions = 1) {
450 for (
unsigned i = 0; i < nRegions; ++i)
451 regions.push_back(state.addRegion());
453 for (
Region *region : regions)
461 return isa<acc::ParallelOp, acc::LoopOp>(op);
468 template <
typename OpTy>
472 LogicalResult matchAndRewrite(OpTy op,
475 Value ifCond = op.getIfCond();
479 IntegerAttr constAttr;
482 if (constAttr.getInt())
483 rewriter.
modifyOpInPlace(op, [&]() { op.getIfCondMutable().erase(0); });
495 assert(llvm::hasSingleElement(region) &&
"expected single-region block");
507 template <
typename OpTy>
508 struct RemoveConstantIfConditionWithRegion :
public OpRewritePattern<OpTy> {
511 LogicalResult matchAndRewrite(OpTy op,
514 Value ifCond = op.getIfCond();
518 IntegerAttr constAttr;
521 if (constAttr.getInt())
522 rewriter.
modifyOpInPlace(op, [&]() { op.getIfCondMutable().erase(0); });
537 Operation *op,
Region ®ion, StringRef regionType, StringRef regionName,
539 if (optional && region.
empty())
543 return op->
emitOpError() <<
"expects non-empty " << regionName <<
" region";
547 return op->
emitOpError() <<
"expects " << regionName
550 << regionType <<
" type";
553 for (YieldOp yieldOp : region.
getOps<acc::YieldOp>()) {
554 if (yieldOp.getOperands().size() != 1 ||
555 yieldOp.getOperands().getTypes()[0] != type)
556 return op->
emitOpError() <<
"expects " << regionName
558 "yield a value of the "
559 << regionType <<
" type";
565 LogicalResult acc::PrivateRecipeOp::verifyRegions() {
567 "privatization",
"init",
getType(),
571 *
this, getDestroyRegion(),
"privatization",
"destroy",
getType(),
581 LogicalResult acc::FirstprivateRecipeOp::verifyRegions() {
583 "privatization",
"init",
getType(),
587 if (getCopyRegion().empty())
588 return emitOpError() <<
"expects non-empty copy region";
593 return emitOpError() <<
"expects copy region with two arguments of the "
594 "privatization type";
596 if (getDestroyRegion().empty())
600 "privatization",
"destroy",
611 LogicalResult acc::ReductionRecipeOp::verifyRegions() {
617 if (getCombinerRegion().empty())
618 return emitOpError() <<
"expects non-empty combiner region";
620 Block &reductionBlock = getCombinerRegion().
front();
624 return emitOpError() <<
"expects combiner region with the first two "
625 <<
"arguments of the reduction type";
627 for (YieldOp yieldOp : getCombinerRegion().getOps<YieldOp>()) {
628 if (yieldOp.getOperands().size() != 1 ||
629 yieldOp.getOperands().getTypes()[0] !=
getType())
630 return emitOpError() <<
"expects combiner region to yield a value "
631 "of the reduction type";
647 if (parser.parseAttribute(attributes.emplace_back()) ||
648 parser.parseArrow() ||
649 parser.parseOperand(operands.emplace_back()) ||
650 parser.parseColonType(types.emplace_back()))
664 std::optional<mlir::ArrayAttr> attributes) {
665 llvm::interleaveComma(llvm::zip(*attributes, operands), p, [&](
auto it) {
666 p << std::get<0>(it) <<
" -> " << std::get<1>(it) <<
" : "
667 << std::get<1>(it).getType();
676 template <
typename Op>
680 if (!mlir::isa<acc::AttachOp, acc::CopyinOp, acc::CopyoutOp, acc::CreateOp,
681 acc::DeleteOp, acc::DetachOp, acc::DevicePtrOp,
682 acc::GetDevicePtrOp, acc::NoCreateOp, acc::PresentOp>(
683 operand.getDefiningOp()))
685 "expect data entry/exit operation or acc.getdeviceptr "
690 template <
typename Op>
694 llvm::StringRef symbolName,
bool checkOperandType =
true) {
695 if (!operands.empty()) {
696 if (!attributes || attributes->size() != operands.size())
698 <<
"expected as many " << symbolName <<
" symbol reference as "
699 << operandName <<
" operands";
703 <<
"unexpected " << symbolName <<
" symbol reference";
708 for (
auto args : llvm::zip(operands, *attributes)) {
711 if (!set.insert(operand).second)
713 << operandName <<
" operand appears more than once";
716 auto symbolRef = llvm::cast<SymbolRefAttr>(std::get<1>(args));
717 auto decl = SymbolTable::lookupNearestSymbolFrom<Op>(op, symbolRef);
720 <<
"expected symbol reference " << symbolRef <<
" to point to a "
721 << operandName <<
" declaration";
723 if (checkOperandType && decl.getType() && decl.getType() != varType)
724 return op->
emitOpError() <<
"expected " << operandName <<
" (" << varType
725 <<
") to be the same type as " << operandName
726 <<
" declaration (" << decl.getType() <<
")";
732 unsigned ParallelOp::getNumDataOperands() {
733 return getReductionOperands().size() + getPrivateOperands().size() +
734 getFirstprivateOperands().size() + getDataClauseOperands().size();
737 Value ParallelOp::getDataOperand(
unsigned i) {
739 numOptional += getNumGangs().size();
740 numOptional += getNumWorkers().size();
741 numOptional += getVectorLength().size();
742 numOptional += getIfCond() ? 1 : 0;
743 numOptional += getSelfCond() ? 1 : 0;
744 return getOperand(getWaitOperands().size() + numOptional + i);
747 template <
typename Op>
749 ArrayAttr deviceTypes,
750 llvm::StringRef keyword) {
751 if (!operands.empty() && deviceTypes.getValue().size() != operands.size())
752 return op.
emitOpError() << keyword <<
" operands count must match "
753 << keyword <<
" device_type count";
757 template <
typename Op>
760 ArrayAttr deviceTypes, llvm::StringRef keyword, int32_t maxInSegment = 0) {
761 std::size_t numOperandsInSegments = 0;
762 std::size_t nbOfSegments = 0;
766 if (maxInSegment != 0 && segCount > maxInSegment)
767 return op.
emitOpError() << keyword <<
" expects a maximum of "
768 << maxInSegment <<
" values per segment";
769 numOperandsInSegments += segCount;
774 if ((numOperandsInSegments != operands.size()) ||
775 (!deviceTypes && !operands.empty()))
777 << keyword <<
" operand count does not match count in segments";
778 if (deviceTypes && deviceTypes.getValue().size() != nbOfSegments)
780 << keyword <<
" segment count does not match device_type count";
785 if (failed(checkSymOperandList<mlir::acc::PrivateRecipeOp>(
786 *
this, getPrivatizations(), getPrivateOperands(),
"private",
787 "privatizations",
false)))
789 if (failed(checkSymOperandList<mlir::acc::FirstprivateRecipeOp>(
790 *
this, getFirstprivatizations(), getFirstprivateOperands(),
791 "firstprivate",
"firstprivatizations",
false)))
793 if (failed(checkSymOperandList<mlir::acc::ReductionRecipeOp>(
794 *
this, getReductionRecipes(), getReductionOperands(),
"reduction",
795 "reductions",
false)))
799 *
this, getNumGangs(), getNumGangsSegmentsAttr(),
800 getNumGangsDeviceTypeAttr(),
"num_gangs", 3)))
804 *
this, getWaitOperands(), getWaitOperandsSegmentsAttr(),
805 getWaitOperandsDeviceTypeAttr(),
"wait")))
809 getNumWorkersDeviceTypeAttr(),
814 getVectorLengthDeviceTypeAttr(),
819 getAsyncOperandsDeviceTypeAttr(),
823 if (failed(checkWaitAndAsyncConflict<acc::ParallelOp>(*
this)))
826 return checkDataOperands<acc::ParallelOp>(*
this, getDataClauseOperands());
832 mlir::acc::DeviceType deviceType) {
835 if (
auto pos =
findSegment(*arrayAttr, deviceType))
840 bool acc::ParallelOp::hasAsyncOnly() {
844 bool acc::ParallelOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {
852 mlir::Value acc::ParallelOp::getAsyncValue(mlir::acc::DeviceType deviceType) {
857 mlir::Value acc::ParallelOp::getNumWorkersValue() {
862 acc::ParallelOp::getNumWorkersValue(mlir::acc::DeviceType deviceType) {
867 mlir::Value acc::ParallelOp::getVectorLengthValue() {
872 acc::ParallelOp::getVectorLengthValue(mlir::acc::DeviceType deviceType) {
874 getVectorLength(), deviceType);
882 ParallelOp::getNumGangsValues(mlir::acc::DeviceType deviceType) {
884 getNumGangsSegments(), deviceType);
887 bool acc::ParallelOp::hasWaitOnly() {
891 bool acc::ParallelOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {
900 ParallelOp::getWaitValues(mlir::acc::DeviceType deviceType) {
902 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),
903 getHasWaitDevnum(), deviceType);
910 mlir::Value ParallelOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {
912 getWaitOperandsSegments(), getHasWaitDevnum(),
928 odsBuilder, odsState, asyncOperands,
nullptr,
929 nullptr, waitOperands,
nullptr,
931 nullptr, numGangs,
nullptr,
933 nullptr, vectorLength,
934 nullptr, ifCond, selfCond,
935 nullptr, reductionOperands,
nullptr,
936 gangPrivateOperands,
nullptr, gangFirstPrivateOperands,
937 nullptr, dataClauseOperands,
953 int32_t crtOperandsSize = operands.size();
956 if (parser.parseOperand(operands.emplace_back()) ||
957 parser.parseColonType(types.emplace_back()))
962 seg.push_back(operands.size() - crtOperandsSize);
986 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
988 p <<
" [" << attr <<
"]";
993 std::optional<mlir::ArrayAttr> deviceTypes,
994 std::optional<mlir::DenseI32ArrayAttr> segments) {
998 llvm::interleaveComma(
999 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](
auto it) {
1000 p << operands[opIdx] <<
" : " << operands[opIdx].getType();
1020 int32_t crtOperandsSize = operands.size();
1024 if (parser.parseOperand(operands.emplace_back()) ||
1025 parser.parseColonType(types.emplace_back()))
1031 seg.push_back(operands.size() - crtOperandsSize);
1057 std::optional<mlir::DenseI32ArrayAttr> segments) {
1059 llvm::interleaveComma(
llvm::enumerate(*deviceTypes), p, [&](
auto it) {
1061 llvm::interleaveComma(
1062 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](
auto it) {
1063 p << operands[opIdx] <<
" : " << operands[opIdx].getType();
1076 mlir::ArrayAttr &keywordOnly) {
1080 bool needCommaBeforeOperands =
false;
1093 if (parser.parseAttribute(keywordAttrs.emplace_back()))
1100 needCommaBeforeOperands =
true;
1103 if (needCommaBeforeOperands && failed(parser.
parseComma()))
1110 int32_t crtOperandsSize = operands.size();
1122 if (parser.parseOperand(operands.emplace_back()) ||
1123 parser.parseColonType(types.emplace_back()))
1129 seg.push_back(operands.size() - crtOperandsSize);
1158 if (attrs->size() != 1)
1160 if (
auto deviceTypeAttr =
1161 mlir::dyn_cast<mlir::acc::DeviceTypeAttr>((*attrs)[0]))
1168 std::optional<mlir::ArrayAttr> deviceTypes,
1169 std::optional<mlir::DenseI32ArrayAttr> segments,
1170 std::optional<mlir::ArrayAttr> hasDevNum,
1171 std::optional<mlir::ArrayAttr> keywordOnly) {
1183 llvm::interleaveComma(
llvm::enumerate(*deviceTypes), p, [&](
auto it) {
1185 auto boolAttr = mlir::dyn_cast<mlir::BoolAttr>((*hasDevNum)[it.index()]);
1186 if (boolAttr && boolAttr.getValue())
1188 llvm::interleaveComma(
1189 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](
auto it) {
1190 p << operands[opIdx] <<
" : " << operands[opIdx].getType();
1206 if (parser.parseOperand(operands.emplace_back()) ||
1207 parser.parseColonType(types.emplace_back()))
1209 if (succeeded(parser.parseOptionalLSquare())) {
1210 if (parser.parseAttribute(attributes.emplace_back()) ||
1211 parser.parseRSquare())
1214 attributes.push_back(mlir::acc::DeviceTypeAttr::get(
1215 parser.getContext(), mlir::acc::DeviceType::None));
1229 std::optional<mlir::ArrayAttr> deviceTypes) {
1232 llvm::interleaveComma(llvm::zip(*deviceTypes, operands), p, [&](
auto it) {
1233 p << std::get<1>(it) <<
" : " << std::get<1>(it).getType();
1242 mlir::ArrayAttr &keywordOnlyDeviceType) {
1245 bool needCommaBeforeOperands =
false;
1251 keywordOnlyDeviceType =
1260 if (parser.parseAttribute(
1261 keywordOnlyDeviceTypeAttributes.emplace_back()))
1268 needCommaBeforeOperands =
true;
1271 if (needCommaBeforeOperands && failed(parser.
parseComma()))
1276 if (parser.parseOperand(operands.emplace_back()) ||
1277 parser.parseColonType(types.emplace_back()))
1279 if (succeeded(parser.parseOptionalLSquare())) {
1280 if (parser.parseAttribute(attributes.emplace_back()) ||
1281 parser.parseRSquare())
1284 attributes.push_back(mlir::acc::DeviceTypeAttr::get(
1285 parser.getContext(), mlir::acc::DeviceType::None));
1291 if (failed(parser.parseRParen()))
1303 std::optional<mlir::ArrayAttr> keywordOnlyDeviceTypes) {
1305 if (operands.begin() == operands.end() &&
1321 mlir::acc::CombinedConstructsTypeAttr &attr) {
1327 parser.
getContext(), mlir::acc::CombinedConstructsType::KernelsLoop);
1330 parser.
getContext(), mlir::acc::CombinedConstructsType::ParallelLoop);
1333 parser.
getContext(), mlir::acc::CombinedConstructsType::SerialLoop);
1336 "expected compute construct name");
1347 mlir::acc::CombinedConstructsTypeAttr attr) {
1349 switch (attr.getValue()) {
1350 case mlir::acc::CombinedConstructsType::KernelsLoop:
1351 p <<
"combined(kernels)";
1353 case mlir::acc::CombinedConstructsType::ParallelLoop:
1354 p <<
"combined(parallel)";
1356 case mlir::acc::CombinedConstructsType::SerialLoop:
1357 p <<
"combined(serial)";
1367 unsigned SerialOp::getNumDataOperands() {
1368 return getReductionOperands().size() + getPrivateOperands().size() +
1369 getFirstprivateOperands().size() + getDataClauseOperands().size();
1372 Value SerialOp::getDataOperand(
unsigned i) {
1374 numOptional += getIfCond() ? 1 : 0;
1375 numOptional += getSelfCond() ? 1 : 0;
1376 return getOperand(getWaitOperands().size() + numOptional + i);
1379 bool acc::SerialOp::hasAsyncOnly() {
1383 bool acc::SerialOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {
1391 mlir::Value acc::SerialOp::getAsyncValue(mlir::acc::DeviceType deviceType) {
1396 bool acc::SerialOp::hasWaitOnly() {
1400 bool acc::SerialOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {
1409 SerialOp::getWaitValues(mlir::acc::DeviceType deviceType) {
1411 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),
1412 getHasWaitDevnum(), deviceType);
1419 mlir::Value SerialOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {
1421 getWaitOperandsSegments(), getHasWaitDevnum(),
1426 if (failed(checkSymOperandList<mlir::acc::PrivateRecipeOp>(
1427 *
this, getPrivatizations(), getPrivateOperands(),
"private",
1428 "privatizations",
false)))
1430 if (failed(checkSymOperandList<mlir::acc::FirstprivateRecipeOp>(
1431 *
this, getFirstprivatizations(), getFirstprivateOperands(),
1432 "firstprivate",
"firstprivatizations",
false)))
1434 if (failed(checkSymOperandList<mlir::acc::ReductionRecipeOp>(
1435 *
this, getReductionRecipes(), getReductionOperands(),
"reduction",
1436 "reductions",
false)))
1440 *
this, getWaitOperands(), getWaitOperandsSegmentsAttr(),
1441 getWaitOperandsDeviceTypeAttr(),
"wait")))
1445 getAsyncOperandsDeviceTypeAttr(),
1449 if (failed(checkWaitAndAsyncConflict<acc::SerialOp>(*
this)))
1452 return checkDataOperands<acc::SerialOp>(*
this, getDataClauseOperands());
1459 unsigned KernelsOp::getNumDataOperands() {
1460 return getDataClauseOperands().size();
1463 Value KernelsOp::getDataOperand(
unsigned i) {
1465 numOptional += getWaitOperands().size();
1466 numOptional += getNumGangs().size();
1467 numOptional += getNumWorkers().size();
1468 numOptional += getVectorLength().size();
1469 numOptional += getIfCond() ? 1 : 0;
1470 numOptional += getSelfCond() ? 1 : 0;
1471 return getOperand(numOptional + i);
1474 bool acc::KernelsOp::hasAsyncOnly() {
1478 bool acc::KernelsOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {
1486 mlir::Value acc::KernelsOp::getAsyncValue(mlir::acc::DeviceType deviceType) {
1491 mlir::Value acc::KernelsOp::getNumWorkersValue() {
1496 acc::KernelsOp::getNumWorkersValue(mlir::acc::DeviceType deviceType) {
1501 mlir::Value acc::KernelsOp::getVectorLengthValue() {
1506 acc::KernelsOp::getVectorLengthValue(mlir::acc::DeviceType deviceType) {
1508 getVectorLength(), deviceType);
1516 KernelsOp::getNumGangsValues(mlir::acc::DeviceType deviceType) {
1518 getNumGangsSegments(), deviceType);
1521 bool acc::KernelsOp::hasWaitOnly() {
1525 bool acc::KernelsOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {
1534 KernelsOp::getWaitValues(mlir::acc::DeviceType deviceType) {
1536 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),
1537 getHasWaitDevnum(), deviceType);
1544 mlir::Value KernelsOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {
1546 getWaitOperandsSegments(), getHasWaitDevnum(),
1552 *
this, getNumGangs(), getNumGangsSegmentsAttr(),
1553 getNumGangsDeviceTypeAttr(),
"num_gangs", 3)))
1557 *
this, getWaitOperands(), getWaitOperandsSegmentsAttr(),
1558 getWaitOperandsDeviceTypeAttr(),
"wait")))
1562 getNumWorkersDeviceTypeAttr(),
1567 getVectorLengthDeviceTypeAttr(),
1572 getAsyncOperandsDeviceTypeAttr(),
1576 if (failed(checkWaitAndAsyncConflict<acc::KernelsOp>(*
this)))
1579 return checkDataOperands<acc::KernelsOp>(*
this, getDataClauseOperands());
1587 if (getDataClauseOperands().empty())
1588 return emitError(
"at least one operand must appear on the host_data "
1591 for (
mlir::Value operand : getDataClauseOperands())
1592 if (!mlir::isa<acc::UseDeviceOp>(operand.getDefiningOp()))
1593 return emitError(
"expect data entry operation as defining op");
1599 results.
add<RemoveConstantIfConditionWithRegion<HostDataOp>>(context);
1611 bool &needCommaBetweenValues,
bool &newValue) {
1618 attributes.push_back(gangArgType);
1619 needCommaBetweenValues =
true;
1630 mlir::ArrayAttr &gangOnlyDeviceType) {
1635 bool needCommaBetweenValues =
false;
1636 bool needCommaBeforeOperands =
false;
1642 gangOnlyDeviceType =
1651 if (parser.parseAttribute(
1652 gangOnlyDeviceTypeAttributes.emplace_back()))
1659 needCommaBeforeOperands =
true;
1663 mlir::acc::GangArgType::Num);
1665 mlir::acc::GangArgType::Dim);
1667 parser.
getContext(), mlir::acc::GangArgType::Static);
1670 if (needCommaBeforeOperands) {
1671 needCommaBeforeOperands =
false;
1678 int32_t crtOperandsSize = gangOperands.size();
1680 bool newValue =
false;
1681 bool needValue =
false;
1682 if (needCommaBetweenValues) {
1690 gangOperands, gangOperandsType,
1691 gangArgTypeAttributes, argNum,
1692 needCommaBetweenValues, newValue)))
1695 gangOperands, gangOperandsType,
1696 gangArgTypeAttributes, argDim,
1697 needCommaBetweenValues, newValue)))
1699 if (failed(
parseGangValue(parser, LoopOp::getGangStaticKeyword(),
1700 gangOperands, gangOperandsType,
1701 gangArgTypeAttributes, argStatic,
1702 needCommaBetweenValues, newValue)))
1705 if (!newValue && needValue) {
1707 "new value expected after comma");
1715 if (gangOperands.empty())
1718 "expect at least one of num, dim or static values");
1724 if (parser.
parseAttribute(deviceTypeAttributes.emplace_back()) ||
1732 seg.push_back(gangOperands.size() - crtOperandsSize);
1740 gangArgTypeAttributes.end());
1745 gangOnlyDeviceTypeAttributes.begin(), gangOnlyDeviceTypeAttributes.end());
1754 std::optional<mlir::ArrayAttr> gangArgTypes,
1755 std::optional<mlir::ArrayAttr> deviceTypes,
1756 std::optional<mlir::DenseI32ArrayAttr> segments,
1757 std::optional<mlir::ArrayAttr> gangOnlyDeviceTypes) {
1759 if (operands.begin() == operands.end() &&
1774 llvm::interleaveComma(
llvm::enumerate(*deviceTypes), p, [&](
auto it) {
1776 llvm::interleaveComma(
1777 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](
auto it) {
1778 auto gangArgTypeAttr = mlir::dyn_cast<mlir::acc::GangArgTypeAttr>(
1779 (*gangArgTypes)[opIdx]);
1780 if (gangArgTypeAttr.getValue() == mlir::acc::GangArgType::Num)
1781 p << LoopOp::getGangNumKeyword();
1782 else if (gangArgTypeAttr.getValue() == mlir::acc::GangArgType::Dim)
1783 p << LoopOp::getGangDimKeyword();
1784 else if (gangArgTypeAttr.getValue() ==
1785 mlir::acc::GangArgType::Static)
1786 p << LoopOp::getGangStaticKeyword();
1787 p <<
"=" << operands[opIdx] <<
" : " << operands[opIdx].getType();
1798 std::optional<mlir::ArrayAttr> segments,
1799 llvm::SmallSet<mlir::acc::DeviceType, 3> &deviceTypes) {
1802 for (
auto attr : *segments) {
1803 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
1804 if (!deviceTypes.insert(deviceTypeAttr.getValue()).second)
1812 llvm::SmallSet<mlir::acc::DeviceType, 3> crtDeviceTypes;
1815 for (
auto attr : deviceTypes) {
1816 auto deviceTypeAttr =
1817 mlir::dyn_cast_or_null<mlir::acc::DeviceTypeAttr>(attr);
1818 if (!deviceTypeAttr)
1820 if (!crtDeviceTypes.insert(deviceTypeAttr.getValue()).second)
1827 if (!getUpperbound().empty() && getInclusiveUpperbound() &&
1828 (getUpperbound().size() != getInclusiveUpperbound()->size()))
1829 return emitError() <<
"inclusiveUpperbound size is expected to be the same"
1830 <<
" as upperbound size";
1833 if (getCollapseAttr() && !getCollapseDeviceTypeAttr())
1834 return emitOpError() <<
"collapse device_type attr must be define when"
1835 <<
" collapse attr is present";
1837 if (getCollapseAttr() && getCollapseDeviceTypeAttr() &&
1838 getCollapseAttr().getValue().size() !=
1839 getCollapseDeviceTypeAttr().getValue().size())
1840 return emitOpError() <<
"collapse attribute count must match collapse"
1841 <<
" device_type count";
1843 return emitOpError()
1844 <<
"duplicate device_type found in collapseDeviceType attribute";
1847 if (!getGangOperands().empty()) {
1848 if (!getGangOperandsArgType())
1849 return emitOpError() <<
"gangOperandsArgType attribute must be defined"
1850 <<
" when gang operands are present";
1852 if (getGangOperands().size() !=
1853 getGangOperandsArgTypeAttr().getValue().size())
1854 return emitOpError() <<
"gangOperandsArgType attribute count must match"
1855 <<
" gangOperands count";
1858 return emitOpError() <<
"duplicate device_type found in gang attribute";
1861 *
this, getGangOperands(), getGangOperandsSegmentsAttr(),
1862 getGangOperandsDeviceTypeAttr(),
"gang")))
1867 return emitOpError() <<
"duplicate device_type found in worker attribute";
1869 return emitOpError() <<
"duplicate device_type found in "
1870 "workerNumOperandsDeviceType attribute";
1872 getWorkerNumOperandsDeviceTypeAttr(),
1878 return emitOpError() <<
"duplicate device_type found in vector attribute";
1880 return emitOpError() <<
"duplicate device_type found in "
1881 "vectorOperandsDeviceType attribute";
1883 getVectorOperandsDeviceTypeAttr(),
1888 *
this, getTileOperands(), getTileOperandsSegmentsAttr(),
1889 getTileOperandsDeviceTypeAttr(),
"tile")))
1893 llvm::SmallSet<mlir::acc::DeviceType, 3> deviceTypes;
1897 return emitError() <<
"only one of \"" << acc::LoopOp::getAutoAttrStrName()
1898 <<
"\", " << getIndependentAttrName() <<
", "
1900 <<
" can be present at the same time";
1905 for (
auto attr : getSeqAttr()) {
1906 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
1907 if (hasVector(deviceTypeAttr.getValue()) ||
1908 getVectorValue(deviceTypeAttr.getValue()) ||
1909 hasWorker(deviceTypeAttr.getValue()) ||
1910 getWorkerValue(deviceTypeAttr.getValue()) ||
1911 hasGang(deviceTypeAttr.getValue()) ||
1912 getGangValue(mlir::acc::GangArgType::Num,
1913 deviceTypeAttr.getValue()) ||
1914 getGangValue(mlir::acc::GangArgType::Dim,
1915 deviceTypeAttr.getValue()) ||
1916 getGangValue(mlir::acc::GangArgType::Static,
1917 deviceTypeAttr.getValue()))
1919 <<
"gang, worker or vector cannot appear with the seq attr";
1923 if (failed(checkSymOperandList<mlir::acc::PrivateRecipeOp>(
1924 *
this, getPrivatizations(), getPrivateOperands(),
"private",
1925 "privatizations",
false)))
1928 if (failed(checkSymOperandList<mlir::acc::ReductionRecipeOp>(
1929 *
this, getReductionRecipes(), getReductionOperands(),
"reduction",
1930 "reductions",
false)))
1933 if (getCombined().has_value() &&
1934 (getCombined().value() != acc::CombinedConstructsType::ParallelLoop &&
1935 getCombined().value() != acc::CombinedConstructsType::KernelsLoop &&
1936 getCombined().value() != acc::CombinedConstructsType::SerialLoop)) {
1937 return emitError(
"unexpected combined constructs attribute");
1941 if (getRegion().empty())
1942 return emitError(
"expected non-empty body.");
1947 unsigned LoopOp::getNumDataOperands() {
1948 return getReductionOperands().size() + getPrivateOperands().size();
1951 Value LoopOp::getDataOperand(
unsigned i) {
1952 unsigned numOptional =
1953 getLowerbound().size() + getUpperbound().size() + getStep().size();
1954 numOptional += getGangOperands().size();
1955 numOptional += getVectorOperands().size();
1956 numOptional += getWorkerNumOperands().size();
1957 numOptional += getTileOperands().size();
1958 numOptional += getCacheOperands().size();
1959 return getOperand(numOptional + i);
1964 bool LoopOp::hasAuto(mlir::acc::DeviceType deviceType) {
1968 bool LoopOp::hasIndependent() {
1972 bool LoopOp::hasIndependent(mlir::acc::DeviceType deviceType) {
1978 bool LoopOp::hasSeq(mlir::acc::DeviceType deviceType) {
1986 mlir::Value LoopOp::getVectorValue(mlir::acc::DeviceType deviceType) {
1988 getVectorOperands(), deviceType);
1993 bool LoopOp::hasVector(mlir::acc::DeviceType deviceType) {
2001 mlir::Value LoopOp::getWorkerValue(mlir::acc::DeviceType deviceType) {
2003 getWorkerNumOperands(), deviceType);
2008 bool LoopOp::hasWorker(mlir::acc::DeviceType deviceType) {
2017 LoopOp::getTileValues(mlir::acc::DeviceType deviceType) {
2019 getTileOperandsSegments(), deviceType);
2022 std::optional<int64_t> LoopOp::getCollapseValue() {
2026 std::optional<int64_t>
2027 LoopOp::getCollapseValue(mlir::acc::DeviceType deviceType) {
2028 if (!getCollapseAttr())
2029 return std::nullopt;
2030 if (
auto pos =
findSegment(getCollapseDeviceTypeAttr(), deviceType)) {
2032 mlir::dyn_cast<IntegerAttr>(getCollapseAttr().getValue()[*pos]);
2033 return intAttr.getValue().getZExtValue();
2035 return std::nullopt;
2038 mlir::Value LoopOp::getGangValue(mlir::acc::GangArgType gangArgType) {
2042 mlir::Value LoopOp::getGangValue(mlir::acc::GangArgType gangArgType,
2043 mlir::acc::DeviceType deviceType) {
2044 if (getGangOperands().empty())
2046 if (
auto pos =
findSegment(*getGangOperandsDeviceType(), deviceType)) {
2047 int32_t nbOperandsBefore = 0;
2048 for (
unsigned i = 0; i < *pos; ++i)
2049 nbOperandsBefore += (*getGangOperandsSegments())[i];
2052 .drop_front(nbOperandsBefore)
2053 .take_front((*getGangOperandsSegments())[*pos]);
2055 int32_t argTypeIdx = nbOperandsBefore;
2056 for (
auto value : values) {
2057 auto gangArgTypeAttr = mlir::dyn_cast<mlir::acc::GangArgTypeAttr>(
2058 (*getGangOperandsArgType())[argTypeIdx]);
2059 if (gangArgTypeAttr.getValue() == gangArgType)
2069 bool LoopOp::hasGang(mlir::acc::DeviceType deviceType) {
2074 return {&getRegion()};
2118 if (!regionArgs.empty()) {
2119 p << acc::LoopOp::getControlKeyword() <<
"(";
2120 llvm::interleaveComma(regionArgs, p,
2122 p <<
") = (" << lowerbound <<
" : " << lowerboundType <<
") to ("
2123 << upperbound <<
" : " << upperboundType <<
") " <<
" step (" << steps
2124 <<
" : " << stepType <<
") ";
2137 if (getOperands().empty() && !getDefaultAttr())
2138 return emitError(
"at least one operand or the default attribute "
2139 "must appear on the data operation");
2141 for (
mlir::Value operand : getDataClauseOperands())
2142 if (!mlir::isa<acc::AttachOp, acc::CopyinOp, acc::CopyoutOp, acc::CreateOp,
2143 acc::DeleteOp, acc::DetachOp, acc::DevicePtrOp,
2144 acc::GetDevicePtrOp, acc::NoCreateOp, acc::PresentOp>(
2145 operand.getDefiningOp()))
2146 return emitError(
"expect data entry/exit operation or acc.getdeviceptr "
2149 if (failed(checkWaitAndAsyncConflict<acc::DataOp>(*
this)))
2155 unsigned DataOp::getNumDataOperands() {
return getDataClauseOperands().size(); }
2157 Value DataOp::getDataOperand(
unsigned i) {
2158 unsigned numOptional = getIfCond() ? 1 : 0;
2160 numOptional += getWaitOperands().size();
2161 return getOperand(numOptional + i);
2164 bool acc::DataOp::hasAsyncOnly() {
2168 bool acc::DataOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {
2176 mlir::Value DataOp::getAsyncValue(mlir::acc::DeviceType deviceType) {
2183 bool DataOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {
2192 DataOp::getWaitValues(mlir::acc::DeviceType deviceType) {
2194 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),
2195 getHasWaitDevnum(), deviceType);
2202 mlir::Value DataOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {
2204 getWaitOperandsSegments(), getHasWaitDevnum(),
2216 if (getDataClauseOperands().empty())
2217 return emitError(
"at least one operand must be present in dataOperands on "
2218 "the exit data operation");
2222 if (getAsyncOperand() && getAsync())
2223 return emitError(
"async attribute cannot appear with asyncOperand");
2227 if (!getWaitOperands().empty() && getWait())
2228 return emitError(
"wait attribute cannot appear with waitOperands");
2230 if (getWaitDevnum() && getWaitOperands().empty())
2231 return emitError(
"wait_devnum cannot appear without waitOperands");
2236 unsigned ExitDataOp::getNumDataOperands() {
2237 return getDataClauseOperands().size();
2240 Value ExitDataOp::getDataOperand(
unsigned i) {
2241 unsigned numOptional = getIfCond() ? 1 : 0;
2242 numOptional += getAsyncOperand() ? 1 : 0;
2243 numOptional += getWaitDevnum() ? 1 : 0;
2244 return getOperand(getWaitOperands().size() + numOptional + i);
2249 results.
add<RemoveConstantIfCondition<ExitDataOp>>(context);
2260 if (getDataClauseOperands().empty())
2261 return emitError(
"at least one operand must be present in dataOperands on "
2262 "the enter data operation");
2266 if (getAsyncOperand() && getAsync())
2267 return emitError(
"async attribute cannot appear with asyncOperand");
2271 if (!getWaitOperands().empty() && getWait())
2272 return emitError(
"wait attribute cannot appear with waitOperands");
2274 if (getWaitDevnum() && getWaitOperands().empty())
2275 return emitError(
"wait_devnum cannot appear without waitOperands");
2277 for (
mlir::Value operand : getDataClauseOperands())
2278 if (!mlir::isa<acc::AttachOp, acc::CreateOp, acc::CopyinOp>(
2279 operand.getDefiningOp()))
2280 return emitError(
"expect data entry operation as defining op");
2285 unsigned EnterDataOp::getNumDataOperands() {
2286 return getDataClauseOperands().size();
2289 Value EnterDataOp::getDataOperand(
unsigned i) {
2290 unsigned numOptional = getIfCond() ? 1 : 0;
2291 numOptional += getAsyncOperand() ? 1 : 0;
2292 numOptional += getWaitDevnum() ? 1 : 0;
2293 return getOperand(getWaitOperands().size() + numOptional + i);
2298 results.
add<RemoveConstantIfCondition<EnterDataOp>>(context);
2317 LogicalResult AtomicUpdateOp::canonicalize(AtomicUpdateOp op,
2324 if (
Value writeVal = op.getWriteOpVal()) {
2334 LogicalResult AtomicUpdateOp::verifyRegions() {
return verifyRegionsCommon(); }
2340 AtomicReadOp AtomicCaptureOp::getAtomicReadOp() {
2341 if (
auto op = dyn_cast<AtomicReadOp>(getFirstOp()))
2343 return dyn_cast<AtomicReadOp>(getSecondOp());
2346 AtomicWriteOp AtomicCaptureOp::getAtomicWriteOp() {
2347 if (
auto op = dyn_cast<AtomicWriteOp>(getFirstOp()))
2349 return dyn_cast<AtomicWriteOp>(getSecondOp());
2352 AtomicUpdateOp AtomicCaptureOp::getAtomicUpdateOp() {
2353 if (
auto op = dyn_cast<AtomicUpdateOp>(getFirstOp()))
2355 return dyn_cast<AtomicUpdateOp>(getSecondOp());
2358 LogicalResult AtomicCaptureOp::verifyRegions() {
return verifyRegionsCommon(); }
2364 template <
typename Op>
2365 static LogicalResult
2367 bool requireAtLeastOneOperand =
true) {
2368 if (operands.empty() && requireAtLeastOneOperand)
2371 "at least one operand must appear on the declare operation");
2374 if (!mlir::isa<acc::CopyinOp, acc::CopyoutOp, acc::CreateOp,
2375 acc::DevicePtrOp, acc::GetDevicePtrOp, acc::PresentOp,
2376 acc::DeclareDeviceResidentOp, acc::DeclareLinkOp>(
2377 operand.getDefiningOp()))
2379 "expect valid declare data entry operation or acc.getdeviceptr "
2383 assert(varPtr &&
"declare operands can only be data entry operations which "
2384 "must have varPtr");
2385 std::optional<mlir::acc::DataClause> dataClauseOptional{
2387 assert(dataClauseOptional.has_value() &&
2388 "declare operands can only be data entry operations which must have "
2392 if (!varPtr.getDefiningOp())
2396 auto declareAttribute{
2398 if (!declareAttribute)
2400 "expect declare attribute on variable in declare operation");
2402 auto declAttr = mlir::cast<mlir::acc::DeclareAttr>(declareAttribute);
2403 if (declAttr.getDataClause().getValue() != dataClauseOptional.value())
2405 "expect matching declare attribute on variable in declare operation");
2412 if (declAttr.getImplicit() &&
2415 "implicitness must match between declare op and flag on variable");
2449 acc::DeviceType dtype) {
2450 unsigned parallelism = 0;
2451 parallelism += (op.hasGang(dtype) || op.getGangDimValue(dtype)) ? 1 : 0;
2452 parallelism += op.hasWorker(dtype) ? 1 : 0;
2453 parallelism += op.hasVector(dtype) ? 1 : 0;
2454 parallelism += op.hasSeq(dtype) ? 1 : 0;
2459 unsigned baseParallelism =
2462 if (baseParallelism > 1)
2463 return emitError() <<
"only one of `gang`, `worker`, `vector`, `seq` can "
2464 "be present at the same time";
2466 for (uint32_t dtypeInt = 0; dtypeInt != acc::getMaxEnumValForDeviceType();
2468 auto dtype =
static_cast<acc::DeviceType
>(dtypeInt);
2473 if (parallelism > 1 || (baseParallelism == 1 && parallelism == 1))
2474 return emitError() <<
"only one of `gang`, `worker`, `vector`, `seq` can "
2475 "be present at the same time";
2482 mlir::ArrayAttr &deviceTypes) {
2487 if (parser.parseAttribute(bindNameAttrs.emplace_back()))
2489 if (failed(parser.parseOptionalLSquare())) {
2490 deviceTypeAttrs.push_back(mlir::acc::DeviceTypeAttr::get(
2491 parser.getContext(), mlir::acc::DeviceType::None));
2493 if (parser.parseAttribute(deviceTypeAttrs.emplace_back()) ||
2494 parser.parseRSquare())
2502 deviceTypes =
ArrayAttr::get(parser.getContext(), deviceTypeAttrs);
2508 std::optional<mlir::ArrayAttr> bindName,
2509 std::optional<mlir::ArrayAttr> deviceTypes) {
2510 llvm::interleaveComma(llvm::zip(*bindName, *deviceTypes), p,
2511 [&](
const auto &pair) {
2512 p << std::get<0>(pair);
2518 mlir::ArrayAttr &gang,
2519 mlir::ArrayAttr &gangDim,
2520 mlir::ArrayAttr &gangDimDeviceTypes) {
2523 gangDimDeviceTypeAttrs;
2524 bool needCommaBeforeOperands =
false;
2537 if (parser.parseAttribute(gangAttrs.emplace_back()))
2544 needCommaBeforeOperands =
true;
2547 if (needCommaBeforeOperands && failed(parser.
parseComma()))
2551 if (parser.parseKeyword(acc::RoutineOp::getGangDimKeyword()) ||
2552 parser.parseColon() ||
2553 parser.parseAttribute(gangDimAttrs.emplace_back()))
2555 if (succeeded(parser.parseOptionalLSquare())) {
2556 if (parser.parseAttribute(gangDimDeviceTypeAttrs.emplace_back()) ||
2557 parser.parseRSquare())
2560 gangDimDeviceTypeAttrs.push_back(mlir::acc::DeviceTypeAttr::get(
2561 parser.getContext(), mlir::acc::DeviceType::None));
2567 if (failed(parser.parseRParen()))
2572 gangDimDeviceTypes =
2579 std::optional<mlir::ArrayAttr> gang,
2580 std::optional<mlir::ArrayAttr> gangDim,
2581 std::optional<mlir::ArrayAttr> gangDimDeviceTypes) {
2584 gang->size() == 1) {
2585 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>((*gang)[0]);
2598 llvm::interleaveComma(llvm::zip(*gangDim, *gangDimDeviceTypes), p,
2599 [&](
const auto &pair) {
2600 p << acc::RoutineOp::getGangDimKeyword() <<
": ";
2601 p << std::get<0>(pair);
2609 mlir::ArrayAttr &deviceTypes) {
2622 if (parser.parseAttribute(attributes.emplace_back()))
2636 std::optional<mlir::ArrayAttr> deviceTypes) {
2639 auto deviceTypeAttr =
2640 mlir::dyn_cast<mlir::acc::DeviceTypeAttr>((*deviceTypes)[0]);
2650 auto dTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
2658 bool RoutineOp::hasWorker(mlir::acc::DeviceType deviceType) {
2664 bool RoutineOp::hasVector(mlir::acc::DeviceType deviceType) {
2670 bool RoutineOp::hasSeq(mlir::acc::DeviceType deviceType) {
2674 std::optional<llvm::StringRef> RoutineOp::getBindNameValue() {
2678 std::optional<llvm::StringRef>
2679 RoutineOp::getBindNameValue(mlir::acc::DeviceType deviceType) {
2681 return std::nullopt;
2682 if (
auto pos =
findSegment(*getBindNameDeviceType(), deviceType)) {
2683 auto attr = (*getBindName())[*pos];
2684 auto stringAttr = dyn_cast<mlir::StringAttr>(attr);
2685 return stringAttr.getValue();
2687 return std::nullopt;
2692 bool RoutineOp::hasGang(mlir::acc::DeviceType deviceType) {
2696 std::optional<int64_t> RoutineOp::getGangDimValue() {
2700 std::optional<int64_t>
2701 RoutineOp::getGangDimValue(mlir::acc::DeviceType deviceType) {
2703 return std::nullopt;
2704 if (
auto pos =
findSegment(*getGangDimDeviceType(), deviceType)) {
2705 auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>((*getGangDim())[*pos]);
2706 return intAttr.getInt();
2708 return std::nullopt;
2719 return emitOpError(
"cannot be nested in a compute operation");
2731 return emitOpError(
"cannot be nested in a compute operation");
2743 return emitOpError(
"cannot be nested in a compute operation");
2744 if (!getDeviceTypeAttr() && !getDefaultAsync() && !getDeviceNum())
2745 return emitOpError(
"at least one default_async, device_num, or device_type "
2746 "operand must appear");
2756 if (getDataClauseOperands().empty())
2757 return emitError(
"at least one value must be present in dataOperands");
2760 getAsyncOperandsDeviceTypeAttr(),
2765 *
this, getWaitOperands(), getWaitOperandsSegmentsAttr(),
2766 getWaitOperandsDeviceTypeAttr(),
"wait")))
2769 if (failed(checkWaitAndAsyncConflict<acc::UpdateOp>(*
this)))
2772 for (
mlir::Value operand : getDataClauseOperands())
2773 if (!mlir::isa<acc::UpdateDeviceOp, acc::UpdateHostOp, acc::GetDevicePtrOp>(
2774 operand.getDefiningOp()))
2775 return emitError(
"expect data entry/exit operation or acc.getdeviceptr "
2781 unsigned UpdateOp::getNumDataOperands() {
2782 return getDataClauseOperands().size();
2785 Value UpdateOp::getDataOperand(
unsigned i) {
2787 numOptional += getIfCond() ? 1 : 0;
2788 return getOperand(getWaitOperands().size() + numOptional + i);
2793 results.
add<RemoveConstantIfCondition<UpdateOp>>(context);
2796 bool UpdateOp::hasAsyncOnly() {
2800 bool UpdateOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {
2808 mlir::Value UpdateOp::getAsyncValue(mlir::acc::DeviceType deviceType) {
2818 bool UpdateOp::hasWaitOnly() {
2822 bool UpdateOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {
2831 UpdateOp::getWaitValues(mlir::acc::DeviceType deviceType) {
2833 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),
2834 getHasWaitDevnum(), deviceType);
2841 mlir::Value UpdateOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {
2843 getWaitOperandsSegments(), getHasWaitDevnum(),
2854 if (getAsyncOperand() && getAsync())
2855 return emitError(
"async attribute cannot appear with asyncOperand");
2857 if (getWaitDevnum() && getWaitOperands().empty())
2858 return emitError(
"wait_devnum cannot appear without waitOperands");
2863 #define GET_OP_CLASSES
2864 #include "mlir/Dialect/OpenACC/OpenACCOps.cpp.inc"
2866 #define GET_ATTRDEF_CLASSES
2867 #include "mlir/Dialect/OpenACC/OpenACCOpsAttributes.cpp.inc"
2869 #define GET_TYPEDEF_CLASSES
2870 #include "mlir/Dialect/OpenACC/OpenACCOpsTypes.cpp.inc"
2879 [&](
auto entry) {
return entry.getVarPtr(); })
2880 .Case<mlir::acc::CopyoutOp, mlir::acc::UpdateHostOp>(
2881 [&](
auto exit) {
return exit.getVarPtr(); })
2889 [&](
auto dataClause) {
return dataClause.getAccPtr(); })
2898 [&](
auto dataClause) {
return dataClause.getVarPtrPtr(); })
2908 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](
auto dataClause) {
2910 dataClause.getBounds().begin(), dataClause.getBounds().end());
2922 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](
auto dataClause) {
2924 dataClause.getAsyncOperands().begin(),
2925 dataClause.getAsyncOperands().end());
2936 return dataClause.getAsyncOperandsDeviceTypeAttr();
2944 [&](
auto dataClause) {
return dataClause.getAsyncOnlyAttr(); })
2951 .Case<ACC_DATA_ENTRY_OPS>([&](
auto entry) {
return entry.getName(); })
2958 std::optional<mlir::acc::DataClause>
2963 .Case<ACC_DATA_ENTRY_OPS>(
2964 [&](
auto entry) {
return entry.getDataClause(); })
2972 [&](
auto entry) {
return entry.getImplicit(); })
2981 [&](
auto entry) {
return entry.getDataClauseOperands(); })
2983 return dataOperands;
2991 [&](
auto entry) {
return entry.getDataClauseOperandsMutable(); })
2993 return dataOperands;
static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op, Region ®ion, ValueRange blockArgs={})
Replaces the given op with the contents of the given single-block region, using the operands of the b...
static MLIRContext * getContext(OpFoldResult val)
static LogicalResult verifyYield(linalg::YieldOp op, LinalgOp linalgOp)
void printRoutineGangClause(OpAsmPrinter &p, Operation *op, std::optional< mlir::ArrayAttr > gang, std::optional< mlir::ArrayAttr > gangDim, std::optional< mlir::ArrayAttr > gangDimDeviceTypes)
static ParseResult parseRegions(OpAsmParser &parser, OperationState &state, unsigned nRegions=1)
bool hasDuplicateDeviceTypes(std::optional< mlir::ArrayAttr > segments, llvm::SmallSet< mlir::acc::DeviceType, 3 > &deviceTypes)
static LogicalResult verifyDeviceTypeCountMatch(Op op, OperandRange operands, ArrayAttr deviceTypes, llvm::StringRef keyword)
LogicalResult checkDeviceTypes(mlir::ArrayAttr deviceTypes)
Check for duplicates in the DeviceType array attribute.
static bool isComputeOperation(Operation *op)
static bool hasOnlyDeviceTypeNone(std::optional< mlir::ArrayAttr > attrs)
static ParseResult parseBindName(OpAsmParser &parser, mlir::ArrayAttr &bindName, mlir::ArrayAttr &deviceTypes)
static void printWaitClause(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > deviceTypes, std::optional< mlir::DenseI32ArrayAttr > segments, std::optional< mlir::ArrayAttr > hasDevNum, std::optional< mlir::ArrayAttr > keywordOnly)
static ParseResult parseWaitClause(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &deviceTypes, mlir::DenseI32ArrayAttr &segments, mlir::ArrayAttr &hasDevNum, mlir::ArrayAttr &keywordOnly)
static bool hasDeviceTypeValues(std::optional< mlir::ArrayAttr > arrayAttr)
static void printDeviceTypeArrayAttr(mlir::OpAsmPrinter &p, mlir::Operation *op, std::optional< mlir::ArrayAttr > deviceTypes)
static ParseResult parseGangValue(OpAsmParser &parser, llvm::StringRef keyword, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, llvm::SmallVector< GangArgTypeAttr > &attributes, GangArgTypeAttr gangArgType, bool &needCommaBetweenValues, bool &newValue)
static ParseResult parseCombinedConstructsLoop(mlir::OpAsmParser &parser, mlir::acc::CombinedConstructsTypeAttr &attr)
static LogicalResult checkDeclareOperands(Op &op, const mlir::ValueRange &operands, bool requireAtLeastOneOperand=true)
static void printDeviceTypes(mlir::OpAsmPrinter &p, std::optional< mlir::ArrayAttr > deviceTypes)
ParseResult parseLoopControl(OpAsmParser &parser, Region ®ion, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &lowerbound, SmallVectorImpl< Type > &lowerboundType, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &upperbound, SmallVectorImpl< Type > &upperboundType, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &step, SmallVectorImpl< Type > &stepType)
loop-control ::= control ( ssa-id-and-type-list ) = ( ssa-id-and-type-list ) to ( ssa-id-and-type-lis...
static LogicalResult checkDataOperands(Op op, const mlir::ValueRange &operands)
Check dataOperands for acc.parallel, acc.serial and acc.kernels.
static ParseResult parseDeviceTypeOperands(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &deviceTypes)
static mlir::Value getValueInDeviceTypeSegment(std::optional< mlir::ArrayAttr > arrayAttr, mlir::Operation::operand_range range, mlir::acc::DeviceType deviceType)
static mlir::Operation::operand_range getValuesFromSegments(std::optional< mlir::ArrayAttr > arrayAttr, mlir::Operation::operand_range range, std::optional< llvm::ArrayRef< int32_t >> segments, mlir::acc::DeviceType deviceType)
static ParseResult parseNumGangs(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &deviceTypes, mlir::DenseI32ArrayAttr &segments)
void printLoopControl(OpAsmPrinter &p, Operation *op, Region ®ion, ValueRange lowerbound, TypeRange lowerboundType, ValueRange upperbound, TypeRange upperboundType, ValueRange steps, TypeRange stepType)
static ParseResult parseDeviceTypeArrayAttr(OpAsmParser &parser, mlir::ArrayAttr &deviceTypes)
static ParseResult parseRoutineGangClause(OpAsmParser &parser, mlir::ArrayAttr &gang, mlir::ArrayAttr &gangDim, mlir::ArrayAttr &gangDimDeviceTypes)
static void printDeviceTypeOperandsWithSegment(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > deviceTypes, std::optional< mlir::DenseI32ArrayAttr > segments)
static void printDeviceTypeOperands(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > deviceTypes)
static void printBindName(mlir::OpAsmPrinter &p, mlir::Operation *op, std::optional< mlir::ArrayAttr > bindName, std::optional< mlir::ArrayAttr > deviceTypes)
static ParseResult parseDeviceTypeOperandsWithSegment(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &deviceTypes, mlir::DenseI32ArrayAttr &segments)
static void printSymOperandList(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > attributes)
static mlir::Operation::operand_range getWaitValuesWithoutDevnum(std::optional< mlir::ArrayAttr > deviceTypeAttr, mlir::Operation::operand_range operands, std::optional< llvm::ArrayRef< int32_t >> segments, std::optional< mlir::ArrayAttr > hasWaitDevnum, mlir::acc::DeviceType deviceType)
static ParseResult parseGangClause(OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &gangOperands, llvm::SmallVectorImpl< Type > &gangOperandsType, mlir::ArrayAttr &gangArgType, mlir::ArrayAttr &deviceType, mlir::DenseI32ArrayAttr &segments, mlir::ArrayAttr &gangOnlyDeviceType)
static LogicalResult verifyInitLikeSingleArgRegion(Operation *op, Region ®ion, StringRef regionType, StringRef regionName, Type type, bool verifyYield, bool optional=false)
static void printSingleDeviceType(mlir::OpAsmPrinter &p, mlir::Attribute attr)
static std::optional< unsigned > findSegment(ArrayAttr segments, mlir::acc::DeviceType deviceType)
static LogicalResult checkSymOperandList(Operation *op, std::optional< mlir::ArrayAttr > attributes, mlir::OperandRange operands, llvm::StringRef operandName, llvm::StringRef symbolName, bool checkOperandType=true)
static void printDeviceTypeOperandsWithKeywordOnly(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > deviceTypes, std::optional< mlir::ArrayAttr > keywordOnlyDeviceTypes)
static bool hasDeviceType(std::optional< mlir::ArrayAttr > arrayAttr, mlir::acc::DeviceType deviceType)
void printGangClause(OpAsmPrinter &p, Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > gangArgTypes, std::optional< mlir::ArrayAttr > deviceTypes, std::optional< mlir::DenseI32ArrayAttr > segments, std::optional< mlir::ArrayAttr > gangOnlyDeviceTypes)
static mlir::Value getWaitDevnumValue(std::optional< mlir::ArrayAttr > deviceTypeAttr, mlir::Operation::operand_range operands, std::optional< llvm::ArrayRef< int32_t >> segments, std::optional< mlir::ArrayAttr > hasWaitDevnum, mlir::acc::DeviceType deviceType)
static ParseResult parseDeviceTypeOperandsWithKeywordOnly(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &deviceTypes, mlir::ArrayAttr &keywordOnlyDeviceType)
static LogicalResult checkWaitAndAsyncConflict(Op op)
static LogicalResult verifyDeviceTypeAndSegmentCountMatch(Op op, OperandRange operands, DenseI32ArrayAttr segments, ArrayAttr deviceTypes, llvm::StringRef keyword, int32_t maxInSegment=0)
static unsigned getParallelismForDeviceType(acc::RoutineOp op, acc::DeviceType dtype)
static void printNumGangs(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > deviceTypes, std::optional< mlir::DenseI32ArrayAttr > segments)
static void printCombinedConstructsLoop(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::acc::CombinedConstructsTypeAttr attr)
static ParseResult parseSymOperandList(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &symbols)
#define ACC_COMPUTE_AND_DATA_CONSTRUCT_OPS
#define ACC_DATA_ENTRY_OPS
#define ACC_DATA_EXIT_OPS
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
virtual ParseResult parseLBrace()=0
Parse a { token.
@ None
Zero or more operands with no delimiters.
virtual ParseResult parseColonTypeList(SmallVectorImpl< Type > &result)=0
Parse a colon followed by a type list, which must have at least one type.
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 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.
virtual ParseResult parseRSquare()=0
Parse a ] token.
virtual ParseResult parseRBrace()=0
Parse a } token.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual ParseResult parseColon()=0
Parse a : token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalLParen()=0
Parse a ( token if present.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseOptionalLSquare()=0
Parse a [ token if present.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
Attributes are known-constant values of operations.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgListType getArguments()
static BoolAttr get(MLIRContext *context, bool value)
MLIRContext is the top-level object for a collection of MLIR operations.
This class provides a mutable adaptor for a range of operands.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region ®ion, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult parseArgumentList(SmallVectorImpl< Argument > &result, Delimiter delimiter=Delimiter::None, bool allowType=false, bool allowAttrs=false)=0
Parse zero or more arguments with a specified surrounding delimiter.
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.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
This class helps build Operations.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Location getLoc()
The source location the operation was defined or derived from.
This provides public APIs that all operations should have.
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
operand_range getOperands()
Returns an iterator on the underlying Value's.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
iterator_range< OpIterator > getOps()
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 replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
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 inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues=std::nullopt)
Inline the operations of block 'source' into block 'dest' before the given position.
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 provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< int32_t > content)
Builder from ArrayRef<T>.
ArrayRef< T > asArrayRef() const
mlir::Value getVarPtr(mlir::Operation *accDataClauseOp)
Used to obtain the varPtr from a data clause operation.
std::optional< mlir::acc::DataClause > getDataClause(mlir::Operation *accDataEntryOp)
Used to obtain the dataClause from a data entry operation.
mlir::MutableOperandRange getMutableDataOperands(mlir::Operation *accOp)
Used to get a mutable range iterating over the data operands.
mlir::SmallVector< mlir::Value > getBounds(mlir::Operation *accDataClauseOp)
Used to obtain bounds from an acc data clause operation.
mlir::ValueRange getDataOperands(mlir::Operation *accOp)
Used to get an immutable range iterating over the data operands.
std::optional< llvm::StringRef > getVarName(mlir::Operation *accOp)
Used to obtain the name from an acc operation.
bool getImplicitFlag(mlir::Operation *accDataEntryOp)
Used to find out whether data operation is implicit.
mlir::SmallVector< mlir::Value > getAsyncOperands(mlir::Operation *accDataClauseOp)
Used to obtain async operands from an acc data clause operation.
mlir::Value getVarPtrPtr(mlir::Operation *accDataClauseOp)
Used to obtain the varPtrPtr from a data clause operation.
mlir::Value getAccPtr(mlir::Operation *accDataClauseOp)
Used to obtain the accPtr from a data clause operation.
mlir::ArrayAttr getAsyncOnly(mlir::Operation *accDataClauseOp)
Returns an array of acc:DeviceTypeAttr attributes attached to an acc data clause operation,...
static constexpr StringLiteral getDeclareAttrName()
Used to obtain the attribute name for declare.
mlir::ArrayAttr getAsyncOperandsDeviceType(mlir::Operation *accDataClauseOp)
Returns an array of acc:DeviceTypeAttr attributes attached to an acc data clause operation,...
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
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...
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
LogicalResult verify(Operation *op, bool verifyRecursively=true)
Perform (potentially expensive) checks of invariants, used to detect compiler bugs,...
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.