42#include "llvm/ADT/STLExtras.h"
43#include "llvm/ADT/ScopeExit.h"
44#include "llvm/ADT/SmallPtrSet.h"
45#include "llvm/ADT/SmallVectorExtras.h"
46#include "llvm/ADT/TypeSwitch.h"
47#include "llvm/Support/DebugLog.h"
48#include "llvm/Support/LogicalResult.h"
55#define DEBUG_TYPE "linalg-transforms"
64 if (
auto attr = dyn_cast<Attribute>(ofr)) {
65 if (!isa<IntegerAttr>(attr))
66 return transformOp.emitDefiniteFailure() <<
"expected IntegerAttr";
71 Value transformValue = cast<Value>(ofr);
72 if (isa<TransformParamTypeInterface>(transformValue.
getType())) {
74 if (params.size() != 1)
75 return transformOp.emitDefiniteFailure()
76 <<
"requires exactly one parameter associated";
77 result.push_back(params[0]);
82 if (!llvm::hasSingleElement(payloadOps)) {
84 transformOp.emitSilenceableError()
85 <<
"handle must be mapped to exactly one payload op";
87 <<
"mapped to " << llvm::range_size(payloadOps) <<
" payload ops";
94 transformOp.emitSilenceableError()
95 <<
"payload op must have exactly 1 index result";
115 if (isa<TransformParamTypeInterface>(packedHandle.
getType())) {
117 for (
auto param : params) {
118 if (!isa<IntegerAttr>(param))
119 return transformOp.emitDefiniteFailure()
120 <<
"expected the parameter to be associated with an integer "
128 if (op->getNumResults() != 1 || !op->getResult(0).getType().isIndex()) {
130 transformOp.emitSilenceableError()
131 <<
"payload op must have exactly 1 index result";
132 diag.attachNote(op->getLoc())
133 <<
"has " << op->getNumResults() <<
" results";
136 result.push_back(op->getResult(0));
150 if (
auto attr = dyn_cast<Attribute>(paramOrHandle)) {
151 reified.push_back(cast<IntegerAttr>(attr).getInt());
154 if (isa<TransformParamTypeInterface>(
155 cast<Value>(paramOrHandle).
getType())) {
157 if (params.size() != 1)
158 return transformOp.emitSilenceableError() <<
"expected a single param";
160 cast<IntegerAttr>(params.front()).getValue().getSExtValue());
164 Value handle = cast<Value>(paramOrHandle);
165 if (!isa<TransformHandleTypeInterface>(handle.getType()))
166 return transformOp.emitSilenceableError() <<
"unexpected value handle";
168 if (!llvm::hasSingleElement(payload))
169 return transformOp.emitSilenceableError()
170 <<
"requires param or handle that is mapped to 1 payload op";
172 Operation *paramOrHandlePayloadOp = *payload.begin();
175 return transformOp.emitSilenceableError()
176 <<
"requires param or handle to be result of op with 1 index "
182 return transformOp.emitSilenceableError()
183 <<
"requires param or handle to be the result of a constant like "
186 reified.push_back(attr.getInt());
195void transform::ApplyEraseUnnecessaryInputsPatternsOp::populatePatterns(
200void transform::ApplyDecomposeTensorPackUnpackPatternsOp::populatePatterns(
205void transform::ApplyDecomposeTensorPadPatternsOp::populatePatterns(
210void transform::ApplyFoldUnitExtentDimsViaReshapesPatternsOp::populatePatterns(
216void transform::ApplyFoldUnitExtentDimsViaSlicesPatternsOp::populatePatterns(
219 options.rankReductionStrategy =
224void transform::ApplyTilingCanonicalizationPatternsOp::populatePatterns(
229void transform::ApplyFoldAddIntoDestPatternsOp::populatePatterns(
234void transform::ApplyPadVectorizationPatternsOp::populatePatterns(
239void transform::ApplyFoldIntoPackAndUnpackPatternsOp::populatePatterns(
244void transform::ApplyFoldPackUnpackIntoEmptyPatternsOp::populatePatterns(
249void transform::ApplyDataLayoutPropagationPatternsOp::populatePatterns(
258void transform::ApplyExtractSliceSinkingPatternsOp::populatePatterns(
262 Operation *producer = opOperand->get().getDefiningOp();
263 Operation *consumer = opOperand->getOwner();
269void transform::ApplySwapExtractSliceWithFillPatternsOp::populatePatterns(
283 SmallVector<Operation *> getNewOps()
const {
284 return SmallVector<Operation *>(newOps.begin(), newOps.end());
288 void notifyOperationInserted(Operation *op,
289 OpBuilder::InsertPoint previous)
override {
290 ForwardingListener::notifyOperationInserted(op, previous);
292 if (previous.
isSet())
296 assert(
inserted.second &&
"expected newly created op");
299 void notifyOperationErased(Operation *op)
override {
300 ForwardingListener::notifyOperationErased(op);
301 op->
walk([&](Operation *op) { newOps.erase(op); });
313 llvm::scope_exit resetListener(
314 [&]() { rewriter.
setListener(previousListener); });
315 NewOpsListener newOpsListener(previousListener);
319 if (getMemcpyOp() ==
"bufferization.materialize_in_destination") {
320 options.memcpyOp = linalg::BufferizeToAllocationOptions::MemcpyOp::
321 MaterializeInDestination;
322 }
else if (getMemcpyOp() ==
"memref.copy") {
325 }
else if (getMemcpyOp() ==
"linalg.copy") {
329 llvm_unreachable(
"invalid memcpy op");
331 if (getAllocOp() ==
"memref.alloc") {
334 }
else if (getAllocOp() ==
"memref.alloca") {
338 llvm_unreachable(
"invalid alloc op");
340 options.bufferizeDestinationOnly = getBufferizeDestinationOnly();
341 options.emitDealloc = getEmitDealloc();
345 getMemorySpace().has_value() ? getMemorySpace().value() :
Attribute();
352 <<
"failed to bufferize operation";
353 diag.attachNote(op->
getLoc()) <<
"target payload op";
356 allocatedBuffers.push_back(buffer);
360 results.
setValues(cast<OpResult>(getAllocatedBuffer()), allocatedBuffers);
361 results.
set(cast<OpResult>(getNewOps()), newOpsListener.getNewOps());
365void transform::BufferizeToAllocationOp::getEffects(
367 if (getBufferizeDestinationOnly()) {
378LogicalResult transform::BufferizeToAllocationOp::verify() {
379 if (getMemcpyOp() !=
"bufferization.materialize_in_destination" &&
380 getMemcpyOp() !=
"memref.copy" && getMemcpyOp() !=
"linalg.copy")
381 return emitOpError() <<
"unsupported memcpy op";
382 if (getAllocOp() !=
"memref.alloc" && getAllocOp() !=
"memref.alloca")
383 return emitOpError() <<
"unsupported alloc op";
395 auto linalgOp = dyn_cast<linalg::LinalgOp>(operand.
getOwner());
402 Value blockArgument = linalgOp.getMatchingBlockArgument(&operand);
410 if (!isa<TensorType, FloatType, IntegerType>(value.
getType()))
412 return llvm::any_of(value.
getUses(),
422 auto type = dyn_cast<RankedTensorType>(
tensor.getType());
424 return emitSilenceableError() <<
"non-tensor type: " <<
tensor;
438 for (
auto [pos, dim] : llvm::enumerate(type.getShape())) {
439 if (!ShapedType::isDynamic(dim))
444 tensor::DimOp::create(rewriter,
tensor.getLoc(),
tensor, cst);
445 preservedOps.insert(dimOp);
446 dynamicDims.push_back(dimOp);
448 auto allocation = bufferization::AllocTensorOp::create(
449 rewriter,
tensor.getLoc(), type, dynamicDims);
451 if (getMemorySpaceAttr())
452 allocation.setMemorySpaceAttr(getMemorySpaceAttr());
453 Value allocated = allocation;
457 if (needsMaterialization) {
458 auto copy = bufferization::MaterializeInDestinationOp::create(
460 preservedOps.insert(
copy);
461 promoted.push_back(
copy.getResult());
463 promoted.push_back(allocated);
467 results.
setValues(cast<OpResult>(getPromoted()), promoted);
471void transform::PromoteTensorOp::getEffects(
487 FailureOr<linalg::LinalgOp> res =
489 if (succeeded(res)) {
493 return emitDefaultSilenceableFailure(
target);
507 auto decomposableOp = dyn_cast<AggregatedOpInterface>(
target);
508 if (!decomposableOp) {
510 "payload is not a decomposable op");
511 return emitDefaultSilenceableFailure(
target);
514 FailureOr<SmallVector<Value>> maybeNewResults =
515 decomposableOp.decomposeOperation(rewriter);
516 if (
failed(maybeNewResults))
517 return emitDefaultSilenceableFailure(
target);
519 rewriter.
replaceOp(decomposableOp, *maybeNewResults);
520 for (
Value val : *maybeNewResults) {
521 Operation *definition = val.getDefiningOp();
532void transform::EliminateLinalgOpAnchoredEmptyTensorsOp::getEffects(
539transform::EliminateLinalgOpAnchoredEmptyTensorsOp::apply(
543 options.allowReturnAllocsFromLoops =
true;
549 <<
"failed to analyze op";
551 rewriter,
target, state)))
553 <<
"failed to eliminate LinalgOp anchored tensor.empty ops";
566 bool applyCleanup,
bool useForall) {
568 builder,
result, loopTypes,
574 applyCleanup, useForall);
580 bool applyCleanup,
bool useForall) {
588 applyCleanup, useForall);
595 bool applyCleanup,
bool useForall) {
599 build(builder,
result, loopTypes,
target, mixedTileSizes,
600 mixedTileInterchange, applyCleanup, useForall);
607 bool applyCleanup,
bool useForall) {
614 staticTileInterchange);
619 auto staticTileInterchangeAttr =
621 unsigned numExpectedLoops =
622 useForall ? 1 : staticTileSizes.size() - llvm::count(staticTileSizes, 0);
624 resultTypes.reserve(numExpectedLoops);
625 assert((loopTypes.size() == 1 || loopTypes.size() == numExpectedLoops) &&
626 "expected one loop type or as many as loops");
627 if (loopTypes.size() == 1)
628 resultTypes.append(numExpectedLoops, loopTypes[0]);
630 llvm::append_range(resultTypes, loopTypes);
635 dynamicTileInterchange,
638 staticTileInterchangeAttr,
646template <
typename Range>
651 function_ref<FailureOr<scf::SCFTileAndFuseResult>(TilingInterface)>
655 size_t numTargets = llvm::range_size(payloadOps);
658 auto tilingInterfaceOp = dyn_cast<TilingInterface>(
target);
659 if (!tilingInterfaceOp)
660 return transformOp->
emitError(
"only TilingInterface ops are supported");
663 FailureOr<scf::SCFTileAndFuseResult> tiledResults =
664 applyFn(tilingInterfaceOp);
665 if (failed(tiledResults))
670 llvm::append_range(opsToReplace, tiledResults->fusedProducers);
671 for (
Operation *toReplace : opsToReplace) {
672 for (
OpResult res : toReplace->getResults())
673 if (
auto replacement = tiledResults->replacements.lookup(res))
675 if (toReplace->use_empty()) {
681 tiledLinalgOps.push_back(tiledResults->tiledAndFusedOps.front());
682 assert(tiledResults->loops.size() == numLoops &&
683 "Mismatched number of loops, tile and fuse transform should have "
685 for (
unsigned int i = 0; i < numLoops; ++i)
686 loopOps[i].
push_back(tiledResults->loops[i]);
689 transformResults.
set(transformOp->
getOpResult(0), tiledLinalgOps);
698 for (
unsigned int idx = 0; idx < numTargets; ++idx)
699 for (
unsigned int i = 0; i < numLoops; ++i)
700 flattenedLoopOps.push_back(loopOps[i][idx]);
701 transformResults.
set(transformOp->
getOpResult(1), flattenedLoopOps);
703 for (
unsigned int i = 0; i < numLoops; ++i)
704 transformResults.
set(transformOp->
getOpResult(i + 1), loopOps[i]);
714 auto transformOp = cast<TransformOpInterface>(getOperation());
720 state, transformOp, mixedTileSizes, getPackedTileSizes())
722 state, transformOp, mixedTileSizes, getMixedTileSizes());
727 state, transformOp, getMixedTileInterchange(), tileInterchange);
731 scf::SCFTilingOptions tilingOptions;
732 tilingOptions.interchangeVector = std::move(tileInterchange);
733 bool useForall = getUseForall();
734 tilingOptions.setLoopType(useForall
735 ? scf::SCFTilingOptions::LoopType::ForallOp
736 : scf::SCFTilingOptions::LoopType::ForOp);
737 tilingOptions.setTileSizes(mixedTileSizes);
738 scf::SCFTileAndFuseOptions tileAndFuseOptions;
739 tileAndFuseOptions.tilingOptions = std::move(tilingOptions);
742 tileAndFuseOptions.tilingOptions.setInnerTileAlignments(
745 if (getApplyCleanup()) {
748 tensor::ExtractSliceOp::getCanonicalizationPatterns(patterns, context);
751 tileAndFuseOptions.cleanupPatterns = std::move(patterns);
758 numLoops = llvm::count_if(mixedTileSizes, [](
OpFoldResult ofr) {
759 auto attr = dyn_cast<Attribute>(ofr);
762 return cast<IntegerAttr>(attr).getInt() != 0;
766 rewriter, getOperation(), state.
getPayloadOps(getTarget()), numLoops,
767 transformResults, getPackedTileSizes() !=
nullptr,
768 [&](TilingInterface tilingInterfaceOp)
769 -> FailureOr<scf::SCFTileAndFuseResult> {
777LogicalResult transform::FuseOp::verify() {
778 bool hasPackedTiles = getPackedTileSizes() !=
nullptr;
779 if (!getMixedTileSizes().empty() && hasPackedTiles)
781 "tile_sizes and packed_tile_sizes are mutually exclusive");
783 auto iterspace_rank = getStaticTileSizes().size();
785 if (permutation.size() > iterspace_rank)
787 <<
"interchange length exceeds iteration space dimensions ("
788 << iterspace_rank <<
"), found " << getTileInterchange();
790 for (
int64_t v : permutation) {
791 if (!ShapedType::isDynamic(v)) {
792 if (v < 0 || v >=
static_cast<int64_t>(iterspace_rank))
793 return emitOpError() <<
"expects interchange values to be in range [0, "
794 << iterspace_rank <<
"), found: " << v;
796 return emitOpError() <<
"found duplicate interchange value: " << v;
802 size_t numExpectedLoops = getUseForall() || hasPackedTiles
804 : sizes.size() - llvm::count(sizes, 0);
805 if (numExpectedLoops != getNumResults() - 1)
806 return emitOpError() <<
"expects " << numExpectedLoops <<
" loop results";
816 return getMixedValues(getStaticTileInterchange(), getTileInterchange(),
820void transform::FuseOp::getEffects(
834void transform::FuseIntoContainingOp::build(
OpBuilder &builder,
837 Value containingOp) {
838 result.addOperands({producerOp, containingOp});
839 auto resultType = transform::AnyOpType::get(builder.
getContext());
840 result.addTypes({resultType, resultType});
856 (domInfo.
dominates(containingOp, user))) {
857 dominatedUsers.insert(user);
860 if (dominatedUsers.empty())
864 auto forallOp = cast<scf::ForallOp>(containingOp);
870 auto genericOp = dyn_cast<linalg::GenericOp>(producerOp);
875 newOuts.push_back(outputs[resultNumber]);
878 auto newforallOp = scf::ForallOp::create(
879 rewriter, loc, forallOp.getMixedLowerBound(),
880 forallOp.getMixedUpperBound(), forallOp.getMixedStep(), newOuts,
881 forallOp.getMapping());
883 newforallOp.getRegion().takeBody(forallOp.getRegion());
888 newforallOp.getBody()->addArgument(newOuts.back().getType(),
889 newOuts.back().getLoc());
890 auto bbArgs = newforallOp.getBody()->getArguments();
893 Operation *op = use.getOwner();
894 return newforallOp->isProperAncestor(op);
898 scf::InParallelOp terminatorOp = newforallOp.getTerminator();
900 terminatorOp.getYieldingOps(), [](
Operation &op) { return &op; });
901 Operation *firstYieldOp = yieldingOps.front();
904 Value dst = newforallOp.getRegionIterArgs().back();
906 tensor::ParallelInsertSliceOp::create(rewriter, firstYieldOp->
getLoc(), src,
907 dst, offsets, sizes, strides);
909 for (
auto result : llvm::enumerate(forallOp.getResults())) {
911 newforallOp->getResult(
result.index()));
914 newforallOp->getResults().back(),
916 Operation *user = use.getOwner();
917 return dominatedUsers.contains(user);
931 destWorklist.push_back(dst);
933 while (!destWorklist.empty()) {
934 Value currentDst = destWorklist.pop_back_val();
938 if (src == currentDst)
943 auto bbArg = dyn_cast<BlockArgument>(currentDst);
947 Block *parentBlock = bbArg.getOwner();
948 assert(parentBlock &&
"unlinked block argument");
951 assert(parentOp &&
"expected block argument with parent operation");
954 auto parentLoop = dyn_cast<LoopLikeOpInterface>(parentOp);
958 for (
auto innerIterArg : parentLoop.getRegionIterArgs()) {
960 OpOperand *operand = parentLoop.getTiedLoopInit(innerIterArg);
961 Value loopBlockArgument =
963 destWorklist.push_back(loopBlockArgument);
976static std::tuple<SmallVector<Operation *>,
Operation *>
980 LDBG() <<
"Try to fuse a direct extract use";
981 auto tileableProducer = dyn_cast<TilingInterface>(producerOp);
982 if (!tileableProducer) {
984 <<
"producer is not a TileableInterface: " << *producerOp;
991 auto it = llvm::find_if(tileableProducer->getUsers(), [&](
Operation *user) {
992 auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user);
993 return sliceOp && containingOp->isProperAncestor(sliceOp);
997 if (it == tileableProducer->getUsers().end()) {
998 diag.attachNote(tileableProducer->getLoc())
999 <<
"could not find fusion opportunity for: " << *tileableProducer;
1002 auto sliceOpToTile = cast<tensor::ExtractSliceOp>(*it);
1015 if (LoopLikeOpInterface containerLoop =
1016 dyn_cast<LoopLikeOpInterface>(sliceOpToTile->getParentOp())) {
1022 auto dpsInterface = dyn_cast<DestinationStyleOpInterface>(
clone);
1026 for (
OpOperand &initOperandPtr : dpsInterface.getDpsInitsMutable()) {
1027 Value producerOperand =
1028 clone->getOperand(initOperandPtr.getOperandNumber());
1030 containerLoop.getRegionIterArgs()) {
1031 OpOperand *bbArg = containerLoop.getTiedLoopInit(containerIterArg);
1032 Value consumerOperand =
1036 initOperandPtr.set(containerIterArg);
1042 tileableProducer = dyn_cast<TilingInterface>(
clone);
1047 cast<OpResult>(sliceOpToTile.getSource()).getResultNumber();
1048 LDBG() <<
"resultNumber: " << resultNumber;
1053 FailureOr<TilingResult> tileAndFuseResult =
1054 tileableProducer.generateResultTileValue(rewriter, resultNumber, offsets,
1055 sizes, innerTileAlignments);
1057 if (failed(tileAndFuseResult)) {
1058 diag.attachNote(tileableProducer->getLoc())
1059 <<
"failed to tile producer op: " << *tileableProducer;
1064 for (
auto *tiledOp : tileAndFuseResult->tiledOps) {
1065 LDBG() <<
"tiledProducer: " << *tiledOp;
1070 auto maybeRankReduced = tensor::ExtractSliceOp::rankReduceIfNeeded(
1071 rewriter, sliceOpToTile->getLoc(), tileAndFuseResult->tiledValues[0],
1072 cast<RankedTensorType>(sliceOpToTile->getResult(0).getType()).getShape());
1073 if (failed(maybeRankReduced)) {
1075 <<
"shape types don't match (missing canonicalization?):\nTiledOp: "
1076 << tileAndFuseResult->tiledValues[0]
1077 <<
"\nSliceOp: " << sliceOpToTile.getOperation() <<
'\n';
1080 rewriter.
replaceOp(sliceOpToTile, *maybeRankReduced);
1084 rewriter,
diag, producerOp, containingOp, *tileAndFuseResult,
1085 resultNumber, offsets, sizes);
1088 if (isa<LoopLikeOpInterface>(containingOp))
1089 rewriter.
eraseOp(tileableProducer);
1091 return std::make_tuple(tileAndFuseResult->tiledOps, newContainingOp);
1104 LDBG() <<
"Try to fuse an extract use through block argument";
1106 auto tileableProducer = dyn_cast<TilingInterface>(producerOp);
1107 if (!tileableProducer) {
1109 <<
"producer is not a TileableInterface: " << *producerOp;
1114 scf::ForallOp forallOp;
1115 auto itProducerUses =
1116 llvm::find_if(tileableProducer->getUses(), [&](
OpOperand &use) {
1117 forallOp = dyn_cast<scf::ForallOp>(use.getOwner());
1121 if (!forallOp || forallOp != containingOp) {
1122 diag.attachNote(tileableProducer->getLoc())
1123 <<
"could not find a use by the containing op: " << *tileableProducer;
1138 auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user);
1139 return sliceOp && containingOp->isProperAncestor(sliceOp);
1143 if (itBBArgUsers == bbArg.
getUsers().end()) {
1145 <<
"could not find fusion opportunity for bbArg: " << bbArg;
1148 auto sliceOpToTile = cast<tensor::ExtractSliceOp>(*itBBArgUsers);
1156 int64_t resultNumber = cast<OpResult>(pUse->
get()).getResultNumber();
1157 LDBG() <<
"resultNumber: " << resultNumber;
1162 rewriter, tileableProducer->getLoc(), tileableProducer,
1163 destinationTensors))) {
1164 diag.attachNote(tileableProducer->getLoc())
1165 <<
"failed to get destination tensors for: " << *tileableProducer;
1170 bvm.
map(destinationTensors[resultNumber], bbArg);
1171 auto tileableProducerClone =
1172 cast<TilingInterface>(rewriter.
clone(*tileableProducer, bvm));
1173 llvm::scope_exit scopeGuard(
1174 [&]() { rewriter.
eraseOp(tileableProducerClone); });
1177 FailureOr<TilingResult> tileAndFuseResult =
1178 tileableProducerClone.generateResultTileValue(
1179 rewriter, resultNumber, sliceOpToTile.getMixedOffsets(),
1180 sliceOpToTile.getMixedSizes(), innerTileAlignments);
1181 if (failed(tileAndFuseResult)) {
1182 diag.attachNote(tileableProducer->getLoc())
1183 <<
"failed to tile producer op: " << *tileableProducer;
1188 auto maybeRankReduced = tensor::ExtractSliceOp::rankReduceIfNeeded(
1189 rewriter, sliceOpToTile->getLoc(), tileAndFuseResult->tiledValues[0],
1190 cast<RankedTensorType>(sliceOpToTile->getResult(0).getType()).getShape());
1191 assert(succeeded(maybeRankReduced) &&
"unexpected shape");
1192 rewriter.
replaceOp(sliceOpToTile, *maybeRankReduced);
1197 destinationTensors.front());
1200 return tileAndFuseResult->tiledOps;
1206 LDBG() <<
"Try to fuse an use by cloning";
1213 uses.push_back(&use);
1218 if (containingOp == use.getOwner()) {
1220 <<
"producer op use by containing op cannot be fused by cloning";
1228 diag.attachNote(producerOp->
getLoc()) <<
"no fusion opportunity by cloning";
1237 assert(!isa<tensor::ParallelInsertSliceOp>(use->
getOwner()) &&
1238 "Parallel insert slice is not a valid clone destination");
1239 unsigned resultNumber = cast<OpResult>(use->
get()).getResultNumber();
1240 LDBG() <<
"resultNumber: " << resultNumber;
1244 fusedOp = rewriter.
clone(*producerOp);
1246 use->
getOwner(), [&] { use->set(fusedOp->getOpResult(resultNumber)); });
1251bool transform::FuseIntoContainingOp::allowsRepeatedHandleOperands() {
1256LogicalResult transform::FuseIntoContainingOp::verify() {
1266 auto containingOps = state.
getPayloadOps(getContainingOp());
1267 if (!llvm::hasSingleElement(containingOps)) {
1269 <<
"requires exactly one containing_op handle (got "
1270 << llvm::range_size(containingOps) <<
")";
1272 Operation *containingOp = *containingOps.begin();
1281 if (std::empty(producerOps)) {
1283 results.
set(cast<OpResult>(getNewContainingOp()), {containingOp});
1290 auto getNextProducer = [&]() -> FailureOr<Operation *> {
1291 for (
const auto &it :
enumerate(remainingProducers)) {
1294 int64_t numUsesInContainingOp =
1296 return containingOp->isAncestor(op);
1301 if (numUsesInContainingOp > 0) {
1302 if (numUsesInContainingOp == 1)
1303 remainingProducers.erase(remainingProducers.begin() + it.index());
1310 while (!remainingProducers.empty()) {
1311 auto nextProducer = getNextProducer();
1312 if (
failed(nextProducer)) {
1314 <<
"could not find next producer to fuse into container";
1315 diag.attachNote(containingOp->
getLoc()) <<
"containing op";
1323 diag <<
"could not fuse " << *producerOp <<
" into " << *containingOp;
1331 rewriter,
diag, producerOp, containingOp, innerTileAlignments);
1332 if (!tiledOps.empty()) {
1333 LDBG() <<
"\nFused a direct extract use\n" << *containingOp;
1334 fusedOps.append(tiledOps);
1335 if (newContainingOp) {
1343 LogicalResult replacementStatus =
1346 (
void)replacementStatus;
1347 assert(succeeded(replacementStatus) &&
1348 "unable to update transform state mapping");
1349 rewriter.
eraseOp(containingOp);
1350 containingOp = newContainingOp;
1357 rewriter,
diag, producerOp, containingOp, innerTileAlignments);
1358 if (!tiledContainingOpOperand.empty()) {
1359 LDBG() <<
"\nFused an extract use through block argument\n"
1361 fusedOps.append(tiledContainingOpOperand);
1368 LDBG() <<
"\nFused an use by cloning\n" << *containingOp;
1369 fusedOps.push_back(cloned);
1375 results.
set(cast<OpResult>(getFusedOp()), fusedOps);
1376 results.
set(cast<OpResult>(getNewContainingOp()), {containingOp});
1380void transform::FuseIntoContainingOp::getEffects(
1398 if (isa<GenericOp>(
target)) {
1404 if (succeeded(generic)) {
1405 results.
push_back(generic->getOperation());
1408 return emitDefaultSilenceableFailure(
target);
1421 if (!isa<GenericOp>(
target)) {
1426 FailureOr<LinalgOp> named =
1428 if (succeeded(named)) {
1429 results.
push_back(named->getOperation());
1432 return emitDefaultSilenceableFailure(
target);
1446 if (interchangeVector.empty()) {
1451 unsigned numLoops = cast<LinalgOp>(
target.getOperation()).getNumLoops();
1452 if (interchangeVector.size() != numLoops) {
1453 return emitSilenceableError()
1454 << getIteratorInterchangeAttrName() <<
" has length ("
1455 << interchangeVector.size()
1456 <<
") different from the number of loops in the target operation ("
1467LogicalResult transform::InterchangeOp::verify() {
1469 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, permutation.size()));
1470 if (!std::is_permutation(sequence.begin(), sequence.end(),
1471 permutation.begin(), permutation.end())) {
1472 return emitOpError()
1473 <<
"expects iterator_interchange to be a permutation, found "
1474 << getIteratorInterchange();
1489 if (!isa<linalg::CopyOp>(targetOp)) {
1491 emitSilenceableError() <<
"only linalg.copy target ops are supported";
1492 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1496 auto copyOp = dyn_cast<linalg::CopyOp>(targetOp);
1497 if (!copyOp.hasPureBufferSemantics()) {
1499 emitSilenceableError()
1500 <<
"cannot transform a linalg.copy on tensors into a memref.copy";
1501 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1507 assert(inputs.size() == 1 &&
"expected linalg copy op with one input");
1508 assert(outputs.size() == 1 &&
"expected memref copy op with one output");
1509 Value input = inputs.front();
1510 Value output = outputs.front();
1515 if (!isa<ShapedType>(input.
getType())) {
1517 emitSilenceableError()
1518 <<
"cannot transform a linalg.copy which input has no shape";
1519 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1524 assert(isa<ShapedType>(output.
getType()));
1526 if (cast<ShapedType>(input.
getType()).getElementType() !=
1527 cast<ShapedType>(output.
getType()).getElementType()) {
1529 emitSilenceableError()
1530 <<
"cannot transform a linalg.copy with different source and "
1531 "destination element types ";
1532 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1553 bool lowerPadLikeWithInsertSlice = getLowerPadLikeWithInsertSlice();
1554 FailureOr<LowerPackResult> res =
1558 <<
"cannot lower to pad + expand + transpose";
1561 transformResults.
push_back(res->expandShapeOp);
1562 transformResults.
push_back(res->transposeOp);
1575 bool lowerUnpadLikeWithExtractSlice = getLowerUnpadLikeWithExtractSlice();
1576 FailureOr<LowerUnPackOpResult> res =
1580 emitSilenceableError()
1581 <<
"cannot lower to transpose + collapse + extract";
1582 diag.attachNote(
target->getLoc()) <<
"target payload op";
1585 transformResults.
push_back(res->emptyOp);
1586 transformResults.
push_back(res->transposeOp);
1587 transformResults.
push_back(res->collapseShapeOp);
1588 transformResults.
push_back(res->extractSliceOp);
1589 transformResults.
push_back(res->copyOp);
1600 result.addAttribute(MatchOp::getOpsAttrName(
result.name),
1609 result.addAttribute(MatchOp::getOpsAttrName(
result.name),
1611 result.addTypes(resultTypes);
1619 if (getOps().has_value())
1620 strs.insert_range(getOps()->getAsValueRange<StringAttr>());
1623 if (!llvm::hasSingleElement(payloadOps)) {
1628 bool incorrectNumOperandTypes =
false;
1635 if (getInterface().has_value()) {
1636 auto iface = getInterface().value();
1637 if (iface == transform::MatchInterfaceEnum::LinalgOp &&
1640 if (iface == transform::MatchInterfaceEnum::TilingInterface &&
1641 !isa<TilingInterface>(op))
1643 if (iface == transform::MatchInterfaceEnum::LoopLikeInterface &&
1644 !isa<LoopLikeOpInterface>(op))
1649 if (getOpAttrs().has_value()) {
1650 DictionaryAttr opAttrs = getOpAttrs().value();
1652 if (attr.getName() == getInterfaceAttrName() ||
1653 attr.getName() == getOpsAttrName())
1655 std::optional<Attribute> inherent = op->
getInherentAttr(attr.getName());
1661 if (actual != attr.getValue())
1666 if (getFilterResultType().has_value()) {
1667 Type t = getFilterResultType().value();
1672 if (getFilterOperandTypes().has_value()) {
1673 mlir::ArrayAttr types = getFilterOperandTypes().value();
1676 if (types.size() == 1) {
1679 dyn_cast<mlir::TypeAttr>(getFilterOperandTypes().value()[0]);
1680 Type t = cast<::mlir::Type>(typeattr.getValue());
1682 [&](
Type operandType) { return operandType == t; }))
1687 if (types.size() != operandTypes.size()) {
1688 incorrectNumOperandTypes =
true;
1692 for (
auto [attr, operandType] :
1693 llvm::zip_equal(getFilterOperandTypes().value(), operandTypes)) {
1694 auto typeattr = cast<mlir::TypeAttr>(attr);
1695 Type type = cast<::mlir::Type>(typeattr.getValue());
1697 if (type != operandType)
1708 (*payloadOps.begin())->walk(matchFun);
1709 if (incorrectNumOperandTypes)
1711 "type, then it must contain as much types as "
1712 "the number of operands in the target ops");
1713 results.
set(cast<OpResult>(getResult()), res);
1728 Type &targetType,
Type &lowSizeType,
1730 Type &splitPointType) {
1731 FunctionType funcType;
1733 if (failed(parser.
parseType<FunctionType>(funcType)))
1736 if (funcType.getNumInputs() != 1 || funcType.getNumResults() != 1) {
1737 parser.
emitError(typeLoc) <<
"expects a trailing functional type with one "
1738 "argument and one result";
1740 targetType = funcType.getInput(0);
1741 lowSizeType = highSizeType = splitPointType = funcType.getResult(0);
1749 if (isa<TransformParamTypeInterface>(getLowSize().
getType())) {
1750 if (
target.hasDynamicShape()) {
1751 auto diag = emitSilenceableError()
1752 <<
"cannot compute parametric tile sizes for dynamically "
1753 "shaped payload op";
1754 diag.attachNote(
target->getLoc()) <<
"payload op";
1759 target, getDimension(), getTargetSize(), getDivisor());
1761 return emitSilenceableError()
1762 <<
"failed to compute multi-size tiling sizes";
1766 results.
assign(llvm::map_range(
1768 spec->lowTileSize * spec->lowTripCount}),
1769 [&builder,
this](
int64_t value) {
1781 builder,
target, getDimension(), targetSize, divisor);
1783 return emitSilenceableError() <<
"could not generate tile size computation";
1790 {spec->lowTileSize, spec->lowTripCount});
1791 Operation *lowTileSize = spec->lowTileSize.getDefiningOp();
1792 Operation *highTileSize = spec->highTileSize.getDefiningOp();
1793 assert(lowTileSize && highTileSize && splitPoint &&
1794 "tile sizes are not produced by operations");
1802void transform::MultiTileSizesOp::getEffects(
1806 if (isa<TransformParamTypeInterface>(getLowSize().
getType()))
1812LogicalResult transform::MultiTileSizesOp::verify() {
1815 return emitOpError() <<
"expects all results type to be the same";
1834 Type linalgOpHType = transform::OperationType::get(
1835 builder.
getContext(), GenericOp::getOperationName());
1854 if (std::empty(targetOps)) {
1855 transformResults.
set(cast<OpResult>(getPackedOp()),
1860 auto linalgOp = dyn_cast<LinalgOp>(*targetOps.begin());
1861 if (!llvm::hasSingleElement(targetOps) || !linalgOp) {
1862 return emitSilenceableError()
1863 <<
"requires target to map to exactly 1 LinalgOp (got "
1864 << llvm::range_size(targetOps) <<
")";
1867 if (getMixedPackedSizes().size() != linalgOp.getNumLoops()) {
1868 return emitSilenceableError()
1869 <<
"requires number of packed sizes match the number of loops ("
1870 << getMixedPackedSizes().size() <<
" vs " << linalgOp.getNumLoops()
1877 state, *
this, packedSizes, getMixedPackedSizes());
1880 FailureOr<PackResult> maybeResult =
pack(rewriter, linalgOp, packedSizes);
1884 transformResults.
set(cast<OpResult>(getPackedOp()),
1885 {maybeResult->packedLinalgOp.getOperation()});
1889void transform::PackOp::getEffects(
1901LogicalResult transform::PackGreedilyOp::verify() {
1903 return emitOpError() << getMatmulInnerDimsOrderAttrName()
1904 <<
" is not a valid permutation";
1907 if (!getMatmulPaddedSizesNextMultipleOf().empty()) {
1908 for (
auto [s, nmo] :
1909 llvm::zip_equal(getMixedMatmulPackedSizes(),
1910 getMatmulPaddedSizesNextMultipleOf())) {
1913 (!maybeStaticPackedSize.has_value() || *maybeStaticPackedSize != 0)) {
1914 return emitOpError() <<
"at most one of the packed_size and the "
1915 "padded_sizes_next_multiple_of can be nonzero "
1916 "for the matmul strategy";
1928 for (
auto linalgOp :
1929 llvm::make_isa_range<LinalgOp>(state.
getPayloadOps(getTarget()))) {
1938 getMixedMatmulPackedSizes(),
1940 getMatmulPaddedSizesNextMultipleOf(),
1941 getMatmulInnerDimsOrder());
1942 if (succeeded(packResult)) {
1943 results.push_back(packResult->packedLinalgOp);
1946 results.push_back(linalgOp);
1948 transformResults.
set(cast<OpResult>(getPackedOp()), results);
1954 return getMixedValues(getStaticMatmulPackedSizes(), getMatmulPackedSizes(),
1958void transform::PackGreedilyOp::getEffects(
1970LogicalResult transform::PackTransposeOp::verify() {
1972 return emitOpError() << getInnerPermAttrName()
1973 <<
" is not a valid permutation";
1976 return emitOpError() << getOuterPermAttrName()
1977 <<
" is not a valid permutation";
1979 if (getInnerPerm().empty() && getOuterPerm().empty()) {
1980 return emitOpError() <<
" at least one of " << getInnerPermAttrName()
1981 <<
" or " << getOuterPermAttrName()
1982 <<
" must be specified";
1988enum class OuterOrInnerPerm { Outer = 0, Inner = 1 };
1998template <
typename RelayoutOpTy>
1999static bool isValidPackingPermutation(
2001 OuterOrInnerPerm outerOrInnerPerm = OuterOrInnerPerm::Outer) {
2003 llvm::is_one_of<RelayoutOpTy, linalg::PackOp, linalg::UnPackOp>::value,
2004 "applies to only pack or unpack operations");
2005 if (!op || permutation.empty())
2007 size_t innerRank = op.getInnerDimsPos().size();
2008 if (outerOrInnerPerm == OuterOrInnerPerm::Inner)
2012 if (std::is_same<RelayoutOpTy, linalg::PackOp>::value) {
2013 return permutation.size() == op.getSourceRank() &&
2016 return permutation.size() == op.getDestRank() &&
2024 auto packOrUnpackOps = state.
getPayloadOps(getTargetPackOrUnPackOp());
2027 if (std::empty(packOrUnpackOps)) {
2028 transformResults.
set(cast<OpResult>(getPackedOp()), {});
2029 transformResults.
set(cast<OpResult>(getPackOp()), {});
2030 transformResults.
set(cast<OpResult>(getUnPackOp()), {});
2036 if (!llvm::hasSingleElement(packOrUnpackOps) ||
2037 !llvm::hasSingleElement(linalgOps)) {
2038 return emitSilenceableError()
2039 <<
"requires target to map to exactly 1 "
2040 "packing op and 1 packed op ("
2041 <<
"got " << llvm::range_size(packOrUnpackOps) <<
" and "
2042 << llvm::range_size(linalgOps) <<
")";
2046 auto packOp = dyn_cast<linalg::PackOp>(*packOrUnpackOps.begin());
2047 auto unPackOp = dyn_cast<linalg::UnPackOp>(*packOrUnpackOps.begin());
2048 if ((!packOp && !unPackOp)) {
2049 return emitSilenceableError() <<
"requires target to map to a "
2050 "linalg.pack or linalg.unpack";
2052 LinalgOp linalgOpTarget = dyn_cast<LinalgOp>(*linalgOps.begin());
2053 if (!linalgOpTarget)
2054 return emitSilenceableError() <<
"requires a LinalgOp target";
2058 if (packOp && packOp.getResult().hasOneUse())
2059 linalgOp = dyn_cast<LinalgOp>(*(packOp.getResult().getUsers().begin()));
2061 linalgOp = unPackOp.getSource().getDefiningOp<LinalgOp>();
2062 if (linalgOp != linalgOpTarget) {
2064 packOp ? StringLiteral{
"not a single use by the LinalgOp target"}
2065 : StringLiteral{
"not produced by the LinalgOp target"};
2066 return emitSilenceableError() << errorMsg;
2072 assert(!packOp &&
"packOp must be null on entry when unPackOp is not null");
2073 OpOperand *packUse = linalgOp.getDpsInitOperand(
2074 cast<OpResult>(unPackOp.getSource()).getResultNumber());
2076 if (!packOp || !packOp.getResult().hasOneUse())
2077 return emitSilenceableError() <<
"could not find matching pack op";
2081 for (
auto permType : {OuterOrInnerPerm::Outer, OuterOrInnerPerm::Inner}) {
2083 (permType == OuterOrInnerPerm::Outer) ? getOuterPerm() : getInnerPerm();
2084 auto errorMsg = (permType == OuterOrInnerPerm::Outer)
2085 ? StringLiteral{
"invalid outer_perm"}
2086 : StringLiteral{
"invalid inner_perm"};
2087 if (!isValidPackingPermutation(packOp, perm, permType) ||
2088 !isValidPackingPermutation(unPackOp, perm, permType)) {
2090 unPackOp ? unPackOp.getOperation() : packOp.getOperation();
2091 return emitSilenceableError() << errorMsg <<
": " << *packOrUnpackOp;
2097 assert(packOp && linalgOp &&
"unexpected null op");
2101 rewriter, packOp, linalgOp, unPackOp, getOuterPerm(), getInnerPerm());
2103 assert(succeeded(res) &&
"unexpected packTranspose failure");
2106 transformResults.
set(cast<OpResult>(getPackOp()), {res->transposedPackOp});
2107 transformResults.
set(cast<OpResult>(getPackedOp()),
2108 {res->transposedLinalgOp});
2110 transformResults.
set(cast<OpResult>(getUnPackOp()),
2111 {res->transposedUnPackOp});
2113 transformResults.
set(cast<OpResult>(getUnPackOp()), {});
2128 StringRef copyBackOp,
2129 bool usePrescribedTensorShapes) {
2130 auto resultType = transform::AnyOpType::get(
b.getContext());
2136 b.getI64ArrayAttr(paddingDimensions),
2139 (padToMultipleOf.empty()
2141 :
b.getDenseI64ArrayAttr(padToMultipleOf)),
2142 b.getI64ArrayAttr(nofoldFlags),
2143 b.getArrayAttr(transposePaddings),
2144 b.getStringAttr(copyBackOp),
2146 usePrescribedTensorShapes ?
b.getUnitAttr() :
nullptr);
2154 StringRef copyBackOp,
2155 bool usePrescribedTensorShapes) {
2156 auto resultType = transform::AnyOpType::get(
b.getContext());
2160 staticPadToMultipleOf);
2166 b.getI64ArrayAttr(paddingDimensions),
2167 dynamicPadToMultipleOf,
2168 staticPadToMultipleOf,
2169 b.getI64ArrayAttr(nofoldFlags),
2170 b.getArrayAttr(transposePaddings),
2172 usePrescribedTensorShapes);
2175void PadOp::getEffects(
2183SmallVector<OpFoldResult> PadOp::getMixedPadToMultipleOf() {
2185 return getMixedValues(getStaticPadToMultipleOf(), getPadToMultipleOf(),
b);
2188DiagnosedSilenceableFailure
2189transform::PadOp::apply(transform::TransformRewriter &rewriter,
2190 transform::TransformResults &results,
2191 transform::TransformState &state) {
2192 auto transformOp = cast<TransformOpInterface>(getOperation());
2193 SmallVector<Operation *> paddedOps, padOps, copyBackOps;
2196 auto linalgTarget = dyn_cast<LinalgOp>(
target);
2197 if (!linalgTarget) {
2198 auto diag = emitSilenceableError() <<
"expected LinalgOp target";
2199 diag.attachNote(
target->getLoc()) <<
"target op";
2204 SmallVector<bool> nofoldFlags;
2205 for (int64_t packPadding :
2207 nofoldFlags.push_back(
static_cast<bool>(packPadding));
2210 SmallVector<Attribute> paddingValues;
2211 for (
auto const &[untypedAttr, elementOrTensorType] :
2212 llvm::zip(getPaddingValues(), linalgTarget->getOperandTypes())) {
2215 paddingValues.push_back(untypedAttr);
2218 auto attr = dyn_cast<TypedAttr>(untypedAttr);
2220 emitOpError(
"expects padding values to be typed attributes or poison");
2225 if (
auto stringAttr = dyn_cast<StringAttr>(attr)) {
2229 if (!parsedAttr || parsedAttr.getType() != elementType) {
2230 auto diag = this->emitOpError(
"expects a padding that parses to ")
2231 << elementType <<
", got " << untypedAttr;
2232 diag.attachNote(linalgTarget.getLoc()) <<
"when applied to this op";
2235 paddingValues.push_back(parsedAttr);
2239 if (attr.getType() != elementType) {
2240 auto diag = this->emitOpError(
"expects a padding value of type ")
2241 << elementType <<
", got " << attr;
2242 diag.attachNote(linalgTarget.getLoc()) <<
"when applied to this op";
2245 paddingValues.push_back(attr);
2249 SmallVector<SmallVector<int64_t>> transposePaddings;
2250 for (Attribute transposeVector : cast<ArrayAttr>(getTransposePaddings()))
2252 cast<ArrayAttr>(transposeVector)));
2259 SmallVector<int64_t> padToMultipleOf;
2261 state, transformOp, getMixedPadToMultipleOf(), padToMultipleOf);
2264 if (padToMultipleOf.empty())
2266 SmallVector<int64_t>(
options.paddingDimensions.size(), 1);
2268 options.padToMultipleOf = std::move(padToMultipleOf);
2269 options.paddingValues = std::move(paddingValues);
2270 options.nofoldFlags = std::move(nofoldFlags);
2271 if (getCopyBackOp() ==
2272 bufferization::MaterializeInDestinationOp::getOperationName()) {
2273 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::
2274 BufferizationMaterializeInDestination;
2275 }
else if (getCopyBackOp() == linalg::CopyOp::getOperationName()) {
2276 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::LinalgCopy;
2277 }
else if (getCopyBackOp() == kCopyOpNone) {
2278 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::None;
2280 llvm_unreachable(
"unsupported copy_back op");
2283 bool irChanged =
false;
2284 if (getUsePrescribedTensorShapes() &&
2285 linalgTarget.hasPureTensorSemantics()) {
2286 OpBuilder::InsertionGuard g(rewriter);
2288 for (OpOperand &operand : linalgTarget->getOpOperands()) {
2289 for (
auto [i, dim] : llvm::enumerate(linalgTarget.getShape(&operand))) {
2290 if (ShapedType::isStatic(dim))
2292 options.setSizeToPadTo(operand.getOperandNumber(), i,
2294 operand.get().getLoc(),
2301 SmallVector<Value> replacements;
2302 SmallVector<tensor::PadOp> newPadOps;
2304 replacements, newPadOps))) {
2310 auto diag = emitSilenceableError() <<
"failed to pad op";
2311 diag.attachNote(
target->getLoc()) <<
"target op";
2320 rewriter.
replaceOp(linalgTarget, replacements);
2321 paddedOps.push_back(paddedOp);
2322 padOps.append(newPadOps.begin(), newPadOps.end());
2323 if (
options.copyBackOp != LinalgPaddingOptions::CopyBackOp::None) {
2324 for (Value v : replacements) {
2325 Operation *copyBackOp = v.getDefiningOp();
2326 if (!llvm::is_contained(copyBackOps, copyBackOp))
2327 copyBackOps.push_back(copyBackOp);
2332 results.
set(cast<OpResult>(getPadded()), paddedOps);
2333 results.
set(cast<OpResult>(getPad()), padOps);
2334 results.
set(cast<OpResult>(getCopy()), copyBackOps);
2338LogicalResult transform::PadOp::verify() {
2339 SmallVector<int64_t> nofoldFlags =
2341 if (any_of(nofoldFlags, [](int64_t packPadding) {
2342 return packPadding != 0 && packPadding != 1;
2344 return emitOpError()
2345 <<
"expects nofold_flags to contain booleans (0/1), found "
2346 << getNofoldFlags();
2349 SmallVector<int64_t> paddingDimensions =
2351 if (any_of(paddingDimensions,
2352 [](int64_t paddingDimension) {
return paddingDimension < 0; })) {
2353 return emitOpError() <<
"expects padding_dimensions to contain positive "
2355 << getPaddingDimensions();
2357 if (!getMixedPadToMultipleOf().empty()) {
2358 if (getMixedPadToMultipleOf().size() != paddingDimensions.size()) {
2359 return emitOpError() <<
"expects as many multiples as padding_dimensions";
2362 ArrayAttr transposes = getTransposePaddings();
2363 for (Attribute attr : transposes) {
2365 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2366 if (!std::is_permutation(sequence.begin(), sequence.end(),
2367 transpose.begin(), transpose.end())) {
2368 return emitOpError()
2369 <<
"expects transpose_paddings to be a permutation, found "
2373 if (getCopyBackOp() !=
2374 bufferization::MaterializeInDestinationOp::getOperationName() &&
2375 getCopyBackOp() != linalg::CopyOp::getOperationName() &&
2376 getCopyBackOp() != kCopyOpNone)
2377 return emitOpError() <<
"invalid copy_back_op";
2385void transform::PadTilingInterfaceOp::build(OpBuilder &
b,
2388 ArrayRef<int64_t> paddingSizes,
2389 bool padToMultipleOf) {
2390 auto resultType = transform::AnyOpType::get(
b.getContext());
2399 :
b.getDenseI64ArrayAttr(paddingSizes)),
2401 padToMultipleOf ?
b.getUnitAttr() :
nullptr);
2404void transform::PadTilingInterfaceOp::build(
2406 ArrayRef<OpFoldResult> mixedPaddingSizes,
bool padToMultipleOf) {
2407 auto resultType = transform::AnyOpType::get(
b.getContext());
2408 SmallVector<int64_t> staticPaddingSizes;
2409 SmallVector<Value> dynamicPaddingSizes;
2411 staticPaddingSizes);
2417 dynamicPaddingSizes,
2422void transform::PadTilingInterfaceOp::getEffects(
2423 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2430SmallVector<OpFoldResult>
2431transform::PadTilingInterfaceOp::getMixedPaddingSizes() {
2436DiagnosedSilenceableFailure
2437transform::PadTilingInterfaceOp::apply(transform::TransformRewriter &rewriter,
2438 transform::TransformResults &results,
2439 transform::TransformState &state) {
2440 SmallVector<Operation *> paddedOps, padOps;
2443 auto targetOp = dyn_cast<TilingInterface>(
target);
2445 auto diag = emitSilenceableError() <<
"expected TilingInterface target";
2446 diag.attachNote(
target->getLoc()) <<
"target op";
2453 if (!isa<IndexingMapOpInterface>(targetOp.getOperation())) {
2454 auto diag = emitSilenceableError() <<
"only IndexingMapOpInterface ops "
2456 diag.attachNote(
target->getLoc()) <<
"target op";
2461 SmallVector<Attribute> paddingValues;
2462 for (
auto const &[untypedAttr, elementOrTensorType] :
2463 llvm::zip(getPaddingValues(), targetOp->getOperandTypes())) {
2464 auto attr = dyn_cast<TypedAttr>(untypedAttr);
2468 paddingValues.push_back(untypedAttr);
2472 emitOpError(
"expects padding values to be typed attributes or poison");
2476 if (
auto stringAttr = dyn_cast<StringAttr>(attr)) {
2480 if (!parsedAttr || parsedAttr.getType() != elementType) {
2481 auto diag = this->emitOpError(
"expects a padding that parses to ")
2482 << elementType <<
", got " << attr;
2483 diag.attachNote(targetOp.getLoc()) <<
"when applied to this op";
2486 paddingValues.push_back(parsedAttr);
2490 if (attr.getType() != elementType) {
2491 auto diag = this->emitOpError(
"expects a padding value of type ")
2492 << elementType <<
", got " << attr;
2493 diag.attachNote(targetOp.getLoc()) <<
"when applied to this op";
2496 paddingValues.push_back(attr);
2500 PadTilingInterfaceOptions
options;
2501 options.paddingValues = std::move(paddingValues);
2502 options.setPaddingSizes(getMixedPaddingSizes())
2503 .setPadToMultipleOf(getPadToMultipleOf());
2505 OpBuilder::InsertionGuard g(rewriter);
2508 rewriter, cast<TilingInterface>(targetOp.getOperation()),
2510 if (
failed(maybePadOps)) {
2511 auto diag = emitSilenceableError() <<
"failed to pad op";
2512 diag.attachNote(
target->getLoc()) <<
"target op";
2515 const auto &[paddedOperands, paddedOp, slicedResults] = maybePadOps.value();
2518 paddedOps.push_back(paddedOp);
2519 padOps.append(paddedOperands.begin(), paddedOperands.end());
2520 rewriter.
replaceOp(targetOp.getOperation(), slicedResults);
2523 results.
set(cast<OpResult>(getPadded()), paddedOps);
2524 results.
set(cast<OpResult>(getPad()), padOps);
2528LogicalResult transform::PadTilingInterfaceOp::verify() {
return success(); }
2534DiagnosedSilenceableFailure transform::HoistPadBuildPackingLoopNestOp::apply(
2535 transform::TransformRewriter &rewriter,
2536 transform::TransformResults &transformResults,
2537 transform::TransformState &state) {
2540 if (!llvm::hasSingleElement(targetOps) || !llvm::hasSingleElement(loopOps)) {
2542 <<
"requires exactly one target and one loop handle (got "
2543 << llvm::range_size(targetOps) <<
" and "
2544 << llvm::range_size(loopOps) <<
")";
2547 auto padOp = dyn_cast_or_null<tensor::PadOp>(*targetOps.begin());
2548 auto loopOp = dyn_cast_or_null<scf::ForOp>(*loopOps.begin());
2549 if (!padOp || !loopOp)
2552 FailureOr<linalg::detail::PackingResult>
result =
2558 if (
result->clonedLoopIvs.empty()) {
2559 transformResults.
set(cast<OpResult>(getPackingLoop()),
2560 {
result->hoistedPadOp.getOperation()});
2563 auto outerPackedLoop =
2565 transformResults.
set(cast<OpResult>(getPackingLoop()),
2566 {outerPackedLoop.getOperation()});
2570LogicalResult transform::HoistPadBuildPackingLoopNestOp::verify() {
2571 ArrayRef<int64_t> transpose = getTranspose();
2572 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2573 if (!std::is_permutation(sequence.begin(), sequence.end(), transpose.begin(),
2575 return emitOpError() <<
"expects transpose to be a permutation, found "
2581void transform::HoistPadBuildPackingLoopNestOp::getEffects(
2582 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2589DiagnosedSilenceableFailure
2590transform::HoistPadOp::applyToOne(transform::TransformRewriter &rewriter,
2592 transform::ApplyToEachResultList &results,
2593 transform::TransformState &state) {
2594 tensor::PadOp hoistedPadOp;
2595 SmallVector<TransposeOp> transposeOps;
2596 FailureOr<Value>
result =
2598 hoistedPadOp, transposeOps);
2609 return emitDefaultSilenceableFailure(
target);
2612LogicalResult transform::HoistPadOp::verify() {
2613 ArrayRef<int64_t> transpose = getTranspose();
2614 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2615 if (!std::is_permutation(sequence.begin(), sequence.end(), transpose.begin(),
2617 return emitOpError() <<
"expects transpose to be a permutation, found "
2627DiagnosedSilenceableFailure
2628transform::PromoteOp::applyToOne(transform::TransformRewriter &rewriter,
2630 transform::ApplyToEachResultList &results,
2631 transform::TransformState &state) {
2632 LinalgPromotionOptions promotionOptions;
2633 if (!getOperandsToPromote().empty())
2636 if (getUseFullTilesByDefault())
2638 getUseFullTilesByDefault());
2639 if (getUseOriginalSubviewSize())
2643 promotionOptions = promotionOptions.
setUseAlloca(getUseAlloca());
2644 if (!getUseFullTileBuffers().empty())
2646 llvm::to_vector(getUseFullTileBuffers().getAsValueRange<BoolAttr>()));
2647 if (getAlignment().has_value())
2648 promotionOptions = promotionOptions.
setAlignment(*getAlignment());
2649 if (getMemorySpace().has_value())
2650 promotionOptions = promotionOptions.
setMemorySpace(*getMemorySpace());
2652 if (getMapping().has_value()) {
2654 auto mapping = *getMapping();
2655 if (mapping.size() > 1)
2656 return emitDefaultDefiniteFailure(
target);
2658 auto addressSpace = cast<mlir::gpu::GPUMemorySpaceMappingAttr>(mapping[0]);
2660 if (addressSpace.getAddressSpace() ==
2661 mlir::gpu::GPUDialect::getWorkgroupAddressSpace()) {
2668 }
else if (addressSpace.getAddressSpace() ==
2669 mlir::gpu::GPUDialect::getPrivateAddressSpace()) {
2677 return emitDefaultDefiniteFailure(
target);
2682 return emitDefaultDefiniteFailure(
target);
2687 return emitDefaultDefiniteFailure(
target);
2696DiagnosedSilenceableFailure
2697transform::ReplaceOp::apply(transform::TransformRewriter &rewriter,
2698 TransformResults &transformResults,
2699 TransformState &state) {
2703 for (Operation *
target : payload) {
2704 if (
target->getNumOperands() > 0)
2706 if (!
target->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
2707 target->getNumRegions() > 0)
2709 <<
"expected target that is isolated from above";
2713 Operation *pattern = &getBodyRegion().front().front();
2714 SmallVector<Operation *> replacements;
2715 for (Operation *
target : payload) {
2716 if (getOperation()->isAncestor(
target))
2723 transformResults.
set(cast<OpResult>(getReplacement()), replacements);
2727void transform::ReplaceOp::getEffects(
2728 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2734LogicalResult transform::ReplaceOp::verify() {
2735 if (!getBodyRegion().hasOneBlock())
2736 return emitOpError() <<
"expected one block";
2737 if (std::distance(getBodyRegion().front().begin(),
2738 getBodyRegion().front().end()) != 1)
2739 return emitOpError() <<
"expected one operation in block";
2740 Operation *
replacement = &getBodyRegion().front().front();
2743 <<
"expected replacement without operands";
2744 if (!
replacement->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
2747 <<
"expect op that is isolated from above";
2755DiagnosedSilenceableFailure
2756transform::ScalarizeOp::applyToOne(transform::TransformRewriter &rewriter,
2758 transform::ApplyToEachResultList &results,
2759 transform::TransformState &state) {
2760 scf::SCFTilingOptions tilingOptions;
2761 tilingOptions.setTileSizeComputationFunction([&](OpBuilder &
b, Operation *) {
2762 SmallVector<OpFoldResult> tileSizes;
2763 Location loc =
target.getLoc();
2764 SmallVector<OpFoldResult> allShapeSizes =
2765 target.createFlatListOfOperandDims(
b, loc);
2766 AffineMap map =
target.getShapesToLoopsMap();
2769 SmallVector<OpFoldResult> shapeSizes =
2774 for (OpFoldResult shapeSize : shapeSizes) {
2776 :
b.getIndexAttr(1));
2781 FailureOr<scf::SCFTilingResult> maybeTilingResult =
tileUsingSCF(
2782 rewriter, cast<TilingInterface>(
target.getOperation()), tilingOptions);
2783 if (
failed(maybeTilingResult))
2784 return emitDefaultDefiniteFailure(
target);
2786 if (
target->getNumResults())
2791 results.
reserve(maybeTilingResult->tiledOps.size());
2792 for (Operation *tiled : maybeTilingResult->tiledOps)
2801DiagnosedSilenceableFailure
2802transform::ConvertToLoopsOp::apply(transform::TransformRewriter &rewriter,
2803 transform::TransformResults &results,
2804 transform::TransformState &state) {
2805 SmallVector<Operation *> loops;
2807 auto tilingOp = dyn_cast<TilingInterface>(*
target);
2809 DiagnosedSilenceableFailure
diag =
2810 emitSilenceableError()
2811 <<
"expected the payload to implement TilingInterface";
2812 diag.attachNote(
target->getLoc()) <<
"payload op";
2816 FailureOr<SmallVector<scf::ForOp>> generatedLoops =
2817 scf::lowerToLoopsUsingSCFForOp(rewriter, tilingOp);
2818 if (
failed(generatedLoops))
2819 return emitDefaultDefiniteFailure(
target);
2820 for (scf::ForOp &loop : *generatedLoops) {
2821 loops.push_back(loop.getOperation());
2825 results.
set(cast<OpResult>(getResult()), loops);
2833DiagnosedSilenceableFailure
2834transform::RewriteInDestinationPassingStyleOp::applyToOne(
2835 transform::TransformRewriter &rewriter, Operation *
target,
2836 transform::ApplyToEachResultList &results,
2837 transform::TransformState &state) {
2839 FailureOr<Operation *> maybeResult =
2841 .Case<DestinationStyleOpInterface>([](
auto op) {
return op; })
2842 .Case<tensor::FromElementsOp, tensor::GenerateOp, tensor::PadOp>(
2843 [&rewriter](
auto op) {
2847 return emitDefaultSilenceableFailure(
target);
2856DiagnosedSilenceableFailure
2857SplitOp::apply(transform::TransformRewriter &rewriter,
2858 TransformResults &results, TransformState &state) {
2860 SmallVector<Operation *> payload =
2863 bool isMultiwaySplit = getMultiway();
2865 if (isMultiwaySplit && !llvm::hasSingleElement(payload)) {
2867 <<
"requires exactly one target when "
2868 "multiway split is enabled (got "
2869 << llvm::range_size(payload) <<
")";
2872 SmallVector<OpFoldResult> chunkSizes;
2874 if (!isMultiwaySplit)
2875 chunkSizes.reserve(payload.size());
2877 if (getDynamicChunkSizes()) {
2879 if (isa<TransformHandleTypeInterface>(getDynamicChunkSizes().
getType())) {
2880 chunkSizes = llvm::map_to_vector(
2881 state.
getPayloadOps(getDynamicChunkSizes()), [&](Operation *op) {
2884 diag = emitSilenceableError()
2885 <<
"expected dynamic split point handle to point to a "
2886 "single-result index-typed op";
2887 diag.attachNote(op->
getLoc()) <<
"dynamic split point";
2892 chunkSizes = llvm::map_to_vector(
2893 state.
getParams(getDynamicChunkSizes()),
2894 [](Attribute attr) {
return OpFoldResult(attr); });
2896 if (
diag.isSilenceableFailure())
2901 if (!isMultiwaySplit && chunkSizes.size() != payload.size()) {
2903 <<
"expected the dynamic split point handle to point to as "
2905 << chunkSizes.size() <<
") as the target handle ("
2906 << payload.size() <<
")";
2909 chunkSizes.resize(payload.size(),
2913 auto checkStructuredOpAndDimensions =
2914 [&](LinalgOp linalgOp, Location loc) -> DiagnosedSilenceableFailure {
2916 auto diag = emitSilenceableError() <<
"only applies to structured ops";
2917 diag.attachNote(loc) <<
"target op";
2921 if (getDimension() >= linalgOp.getNumLoops()) {
2922 auto diag = emitSilenceableError() <<
"dimension " << getDimension()
2923 <<
" does not exist in target op";
2924 diag.attachNote(loc) <<
"target op";
2930 auto checkFailureInSplitting =
2931 [&](
bool hasFailed, Location loc) -> DiagnosedSilenceableFailure {
2940 SmallVector<Operation *> opList;
2941 if (isMultiwaySplit) {
2944 TilingInterface head, tail;
2945 Operation *
target = payload.front();
2947 LinalgOp linalgOp = dyn_cast<LinalgOp>(
target);
2950 DiagnosedSilenceableFailure
diag =
2951 checkStructuredOpAndDimensions(linalgOp,
target->getLoc());
2952 if (
diag.isSilenceableFailure())
2955 for (
auto &&[idx, chunkSize] : llvm::enumerate(chunkSizes)) {
2958 target = tail.getOperation();
2963 linalgOp = cast<LinalgOp>(
target);
2964 Location loc =
target->getLoc();
2968 rewriter, cast<TilingInterface>(linalgOp.getOperation()),
2969 getDimension(), chunkSize);
2972 DiagnosedSilenceableFailure
diag =
2973 checkFailureInSplitting(!head && !tail, loc);
2974 if (
diag.isDefiniteFailure())
2977 opList.push_back(head.getOperation());
2982 opList.push_back(tail.getOperation());
2986 SmallVector<Operation *> first, second;
2987 Operation *noSecondPart =
nullptr;
2988 for (
const auto &pair : llvm::zip(payload, chunkSizes)) {
2989 Operation *
target = std::get<0>(pair);
2990 Location loc =
target->getLoc();
2991 LinalgOp linalgOp = dyn_cast<LinalgOp>(
target);
2992 DiagnosedSilenceableFailure
diag =
2993 checkStructuredOpAndDimensions(linalgOp,
target->getLoc());
2995 if (
diag.isSilenceableFailure())
2999 std::tie(first.emplace_back(), second.emplace_back()) =
linalg::splitOp(
3000 rewriter, cast<TilingInterface>(linalgOp.getOperation()),
3001 getDimension(), std::get<1>(pair));
3004 DiagnosedSilenceableFailure diagSplit =
3005 checkFailureInSplitting(!first.back() && !second.back(), loc);
3010 if (!second.back()) {
3016 if (second.size() != first.size() && !second.empty()) {
3017 auto diag = emitSilenceableError()
3018 <<
"splitting does not produce the second part for a subset "
3021 <<
"expected splitting to produce the second part of all "
3022 "or none of the targets";
3024 <<
"first target with no second part";
3028 opList.append(first);
3029 if (!second.empty())
3030 opList.append(second);
3032 results.
set(cast<OpResult>(getSplitList()), opList);
3036void SplitOp::getEffects(
3037 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3039 if (getDynamicChunkSizes())
3045ParseResult SplitOp::parse(OpAsmParser &parser, OperationState &
result) {
3046 OpAsmParser::UnresolvedOperand
target, dynamicChunkSizes;
3047 IntegerAttr staticChunkSizes;
3051 OptionalParseResult dynamicPointParseResult =
3053 if (!dynamicPointParseResult.
has_value()) {
3054 int64_t staticChunkSizesValue;
3068 if (dynamicPointParseResult.
has_value()) {
3069 Type chunkSizesType;
3082 SplitOp::getStaticChunkSizesAttrName(
result.name).getValue(),
3084 result.addTypes(targetType);
3088void SplitOp::print(OpAsmPrinter &printer) {
3089 printer <<
" " << getTarget() <<
" after ";
3090 int64_t staticChunkSize =
static_cast<int64_t
>(getStaticChunkSizes());
3091 if (staticChunkSize != ShapedType::kDynamic)
3092 printer << staticChunkSize;
3094 printer << getDynamicChunkSizes();
3096 NamedAttrList attrs(getOperation()->getDiscardableAttrDictionary());
3097 attrs.append(getDimensionAttrName(), getDimensionAttr());
3098 if (UnitAttr multiway = getMultiwayAttr())
3099 attrs.append(getMultiwayAttrName(), multiway);
3101 printer <<
" : " << getTarget().getType();
3102 if (staticChunkSize == ShapedType::kDynamic)
3103 printer <<
", " << getDynamicChunkSizes().getType();
3106LogicalResult SplitOp::verify() {
3107 if ((
static_cast<int64_t
>(getStaticChunkSizes()) != ShapedType::kDynamic) ^
3108 (getDynamicChunkSizes() ==
nullptr)) {
3109 return emitOpError() <<
"expects either a dynamic or a static split "
3110 "point to be provided";
3119void transform::SplitReductionOp::build(
3120 OpBuilder &builder, OperationState &
result, Value
target,
3121 int64_t splitFactor, int64_t insertSplitDimension,
bool innerParallel,
3122 bool useScalingAlgorithm,
bool useAlloc) {
3125 result.addAttribute(SplitReductionOp::getSplitFactorAttrName(
result.name),
3128 SplitReductionOp::getInsertSplitDimensionAttrName(
result.name),
3130 if (innerParallel) {
3131 result.addAttribute(SplitReductionOp::getInnerParallelAttrName(
result.name),
3134 if (useScalingAlgorithm) {
3136 SplitReductionOp::getUseScalingAlgorithmAttrName(
result.name),
3140 result.addAttribute(SplitReductionOp::getUseAllocAttrName(
result.name),
3143 auto resultType = transform::AnyOpType::get(ctx);
3144 result.addTypes({resultType, resultType, resultType, resultType});
3147DiagnosedSilenceableFailure transform::SplitReductionOp::applyToOne(
3148 transform::TransformRewriter &rewriter, LinalgOp
target,
3149 transform::ApplyToEachResultList &results,
3150 transform::TransformState &state) {
3152 return linalg::SplitReductionOptions{int64_t(getSplitFactor()),
3153 unsigned(getInsertSplitDimension()),
3154 bool(getInnerParallel())};
3157 FailureOr<SplitReductionResult> splitResult =
3158 (getUseScalingAlgorithm())
3162 return emitDefaultDefiniteFailure(
target);
3164 results.
push_back(splitResult->initOrAlloc);
3166 results.
push_back(splitResult->splitLinalgOp);
3167 results.
push_back(splitResult->resultCombiningLinalgOp);
3175void transform::TileReductionUsingForOp::build(
3176 OpBuilder &builder, OperationState &
result, Value
target,
3177 ArrayRef<int64_t> staticTileSizes) {
3184 auto opTy = transform::AnyOpType::get(ctx);
3190 staticTileSizesAttr);
3193DiagnosedSilenceableFailure transform::TileReductionUsingForOp::applyToOne(
3194 transform::TransformRewriter &rewriter, Operation *
target,
3195 transform::ApplyToEachResultList &results,
3196 transform::TransformState &state) {
3199 auto partialReductionOp = dyn_cast<PartialReductionOpInterface>(
target);
3200 if (!partialReductionOp) {
3203 "Operation should implement PartialReductionOpInterface");
3206 SmallVector<unsigned> reductionDims =
3208 if (reductionDims.empty()) {
3209 for (
auto [idx, iteratorType] :
3210 llvm::enumerate(partialReductionOp.getLoopIteratorTypes())) {
3211 if (iteratorType == utils::IteratorType::reduction)
3212 reductionDims.push_back(idx);
3216 scf::SCFTilingOptions
options;
3217 options.setLoopType(scf::SCFTilingOptions::LoopType::ForOp);
3218 options.setReductionTilingStrategy(
3221 options.setReductionDims(reductionDims);
3222 FailureOr<scf::SCFTilingResult>
result =
3223 scf::tileUsingSCF(rewriter, partialReductionOp,
options);
3227 "failed to tile using partial reduction");
3230 for (Value initValue :
result->initialValues)
3232 for (
auto *parallelTiledOp :
result->tiledOps)
3234 for (
auto *mergeOp :
result->mergeOps)
3244void transform::TileReductionUsingForallOp::build(
3245 OpBuilder &builder, OperationState &
result, Value
target,
3246 ArrayRef<int64_t> staticNumThreads, ArrayRef<int64_t> staticTileSizes,
3254 auto opTy = transform::AnyOpType::get(ctx);
3261 staticNumThreadsAttr,
3262 staticTileSizesAttr,
3266DiagnosedSilenceableFailure transform::TileReductionUsingForallOp::applyToOne(
3267 transform::TransformRewriter &rewriter, Operation *
target,
3268 transform::ApplyToEachResultList &results,
3269 transform::TransformState &state) {
3272 auto partialReductionOp = dyn_cast<PartialReductionOpInterface>(
target);
3273 if (!partialReductionOp) {
3276 "Operation should implement PartialReductionOpInterface");
3278 SmallVector<OpFoldResult> numThreads =
3280 SmallVector<OpFoldResult> tileSizes =
3283 scf::SCFTilingOptions
options;
3284 options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
3285 options.setReductionTilingStrategy(
3287 if (!getNumThreads().empty()) {
3288 options.setNumThreads(numThreads);
3290 options.setTileSizes(tileSizes);
3292 if (
auto mapping = getMapping()) {
3293 options.setMapping(mapping.value().getValue());
3295 SmallVector<unsigned> reductionDims =
3297 if (reductionDims.empty()) {
3298 for (
auto [idx, iteratorType] :
3299 llvm::enumerate(partialReductionOp.getLoopIteratorTypes())) {
3300 if (iteratorType == utils::IteratorType::reduction)
3301 reductionDims.push_back(idx);
3304 options.setReductionDims(reductionDims);
3305 FailureOr<scf::SCFTilingResult>
result =
3306 scf::tileUsingSCF(rewriter, partialReductionOp,
options);
3309 auto diag = emitSilenceableError() <<
"could not tile reduction";
3314 for (Value initValue :
result->initialValues)
3316 for (
auto *parallelTiledOp :
result->tiledOps)
3318 for (
auto *mergeOp :
result->mergeOps)
3328DiagnosedSilenceableFailure
3329transform::ContinuousTileSizesOp::apply(transform::TransformRewriter &rewriter,
3330 TransformResults &transformResults,
3331 TransformState &state) {
3333 SmallVector<Operation *> targetOps =
3336 if (!llvm::hasSingleElement(targetOps)) {
3338 <<
"requires exactly one target (got " << llvm::range_size(targetOps)
3342 Operation *
target = *targetOps.begin();
3343 auto linalgOp = dyn_cast<LinalgOp>(
target);
3344 auto tileableOp = dyn_cast<TilingInterface>(
target);
3349 OpBuilder builder(linalgOp.getContext());
3351 if (isa<TransformParamTypeInterface>(getChunkSizes().
getType())) {
3352 if (linalgOp.hasDynamicShape()) {
3353 auto diag = emitSilenceableError()
3354 <<
"cannot compute parametric tile sizes for dynamically "
3355 "shaped payload op";
3356 diag.attachNote(linalgOp->getLoc()) <<
"payload op";
3360 FailureOr<StaticContinuousTileSizeSpecification> spec =
3364 return emitSilenceableError()
3365 <<
"failed to compute multi-size tiling sizes";
3368 SmallVector<int64_t> chunkSizes;
3370 for (
auto &&[tileSize, tripCount] :
3371 llvm::zip_equal(spec->tileSizes, spec->tripCounts))
3372 chunkSizes.push_back(tileSize * tripCount);
3374 auto getI64AttrsFromI64 = [&](ArrayRef<int64_t> values) {
3375 return llvm::map_to_vector(values, [&](int64_t value) -> Attribute {
3380 getI64AttrsFromI64(spec->tileSizes));
3381 transformResults.
setParams(cast<OpResult>(getChunkSizes()),
3382 getI64AttrsFromI64(chunkSizes));
3389 OpFoldResult targetSize = builder.
getIndexAttr(getTargetSize());
3390 unsigned dimension = getDimension();
3393 builder, tileableOp, dimension, targetSize,
true);
3395 return emitSilenceableError() <<
"could not generate tile size computation";
3400 auto apply = [&](AffineExpr expr, ArrayRef<OpFoldResult> ofrs) -> Value {
3405 SmallVector<Value> chunkSizes;
3407 for (
auto &&[tileSize, tripCount] :
3408 llvm::zip_equal(spec->tileSizes, spec->tripCounts)) {
3409 splitPoint = apply(s0 * s1, {tileSize, tripCount});
3410 chunkSizes.push_back(splitPoint);
3413 auto getDefiningOps = [&](ArrayRef<Value> values) {
3414 return llvm::map_to_vector(values, [&](Value value) -> Operation * {
3420 getDefiningOps(spec->tileSizes));
3421 transformResults.
set(cast<OpResult>(getChunkSizes()),
3422 getDefiningOps(chunkSizes));
3427LogicalResult transform::ContinuousTileSizesOp::verify() {
3430 return emitOpError() <<
"expects all results type to be the same";
3436void transform::ContinuousTileSizesOp::getEffects(
3437 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3454 Type &tileSizesType,
3455 Type &chunkSizesType) {
3456 FunctionType funcType;
3458 if (failed(parser.
parseType<FunctionType>(funcType)))
3461 if (funcType.getNumInputs() != 1 || funcType.getNumResults() != 1) {
3462 parser.
emitError(typeLoc) <<
"expects a trailing functional type with one "
3463 "argument and one result";
3465 targetType = funcType.getInput(0);
3466 tileSizesType = chunkSizesType = funcType.getResult(0);
3475void transform::TileUsingForOp::build(
3477 Value
target, ArrayRef<int64_t> staticTileSizes,
3478 ArrayRef<int64_t> interchange,
3479 std::optional<ArrayRef<bool>> scalableSizes) {
3480 return build(builder,
result, loopTypes,
3484 interchange, scalableSizes);
3487void transform::TileUsingForOp::build(
3488 OpBuilder &builder, OperationState &
result, Value
target,
3489 ArrayRef<int64_t> staticTileSizes, ArrayRef<int64_t> interchange,
3490 std::optional<ArrayRef<bool>> scalableSizes) {
3493 interchange, scalableSizes);
3496void transform::TileUsingForOp::build(
3497 OpBuilder &builder, OperationState &
result, Value
target,
3498 ArrayRef<OpFoldResult> mixedTileSizes, ArrayRef<int64_t> interchange,
3499 std::optional<ArrayRef<bool>> scalableSizes) {
3502 SmallVector<Type> loopTypes(1, builder.
getType<transform::AnyOpType>());
3503 build(builder,
result, loopTypes,
target, mixedTileSizes, interchange,
3507void transform::TileUsingForOp::build(
3508 OpBuilder &builder, OperationState &
result, Value
target,
3509 ArrayRef<OpFoldResult> mixedTileSizes,
3510 ArrayRef<OpFoldResult> mixedInterchange,
3511 std::optional<ArrayRef<bool>> scalableSizes) {
3514 SmallVector<Type> loopTypes(1, builder.
getType<transform::AnyOpType>());
3515 build(builder,
result, loopTypes,
target, mixedTileSizes, mixedInterchange,
3519void transform::TileUsingForOp::build(
3521 Value
target, ArrayRef<OpFoldResult> mixedTileSizes,
3522 ArrayRef<int64_t> interchange,
3523 std::optional<ArrayRef<bool>> scalableSizes) {
3524 SmallVector<OpFoldResult> mixedInterchange =
3526 build(builder,
result, loopTypes,
target, mixedTileSizes, mixedInterchange,
3530void transform::TileUsingForOp::build(
3532 Value
target, ArrayRef<OpFoldResult> mixedTileSizes,
3533 ArrayRef<OpFoldResult> mixedInterchange,
3534 std::optional<ArrayRef<bool>> scalableSizes) {
3535 SmallVector<int64_t> staticTileSizes;
3536 SmallVector<Value> dynamicTileSizes;
3537 SmallVector<int64_t> staticInterchange;
3538 SmallVector<Value> dynamicInterchange;
3547 unsigned numExpectedLoops =
3548 staticTileSizes.size() - llvm::count(staticTileSizes, 0);
3549 SmallVector<Type> resultTypes;
3550 resultTypes.reserve(numExpectedLoops);
3551 assert((loopTypes.size() == 1 || loopTypes.size() == numExpectedLoops) &&
3552 "expected one loop type or as many as loops");
3553 if (loopTypes.size() == 1)
3554 resultTypes.append(numExpectedLoops, loopTypes[0]);
3556 llvm::append_range(resultTypes, loopTypes);
3557 SmallVector<bool> expandedScalableSizes(mixedTileSizes.size(),
false);
3558 if (scalableSizes.has_value())
3559 expandedScalableSizes.assign(scalableSizes->begin(), scalableSizes->end());
3560 Value packedTileSizes;
3568 staticTileSizesAttr,
3569 staticInterchangeAttr,
3570 expandedScalableSizes);
3573LogicalResult transform::TileUsingForOp::verify() {
3574 bool hasPackedTiles = getPackedTileSizes() != Value();
3575 bool hasPackedInterchange = getPackedInterchange() != Value();
3578 "tile_sizes and packed_tile_sizes are mutually exclusive");
3579 if (!getMixedInterchange().empty() && hasPackedInterchange)
3581 "interchange and packed_interchange are mutually exclusive");
3582 if (hasPackedTiles && !getScalableSizes().empty())
3584 "scalable tile_sizes are not supported with packed_tile_sizes");
3587 return emitOpError(
"expected same number of sizes (")
3589 << getScalableSizes().size() <<
")";
3591 auto iterspaceRank = getStaticSizes().size();
3592 ArrayRef<int64_t> permutation = getStaticInterchange();
3593 if (permutation.size() > iterspaceRank)
3594 return emitOpError()
3595 <<
"interchange length exceeds iteration space dimensions ("
3596 << iterspaceRank <<
"), found " << getInterchange();
3597 SmallVector<bool> seen(iterspaceRank,
false);
3598 for (int64_t v : permutation) {
3599 if (!ShapedType::isDynamic(v)) {
3600 if (v < 0 || v >=
static_cast<int64_t
>(iterspaceRank))
3601 return emitOpError() <<
"expects interchange values to be in range [0, "
3602 << iterspaceRank <<
"), found: " << v;
3604 return emitOpError() <<
"found duplicate interchange value: " << v;
3609 ArrayRef<int64_t> staticSizes = getStaticSizes();
3610 unsigned numExpectedLoops =
3611 hasPackedTiles ? 1 : staticSizes.size() - llvm::count(staticSizes, 0);
3612 if (getLoops().size() != numExpectedLoops)
3613 return emitOpError(
"expected number of loops to tile (")
3614 << numExpectedLoops <<
") to match number of `loops` results ("
3615 << getLoops().size() <<
")";
3619DiagnosedSilenceableFailure
3620transform::TileUsingForOp::apply(transform::TransformRewriter &rewriter,
3621 TransformResults &transformResults,
3622 TransformState &state) {
3623 ArrayRef<int64_t> tileSizes = getStaticSizes();
3624 bool hasPackedTiles = getPackedTileSizes() != Value();
3625 bool hasPackedInterchange = getPackedInterchange() != Value();
3626 auto transformOp = cast<TransformOpInterface>(getOperation());
3628 SmallVector<OpFoldResult> mixedInterchange;
3629 if (hasPackedInterchange) {
3630 DiagnosedSilenceableFailure status =
3632 state, transformOp, mixedInterchange, getPackedInterchange());
3636 mixedInterchange = getMixedInterchange();
3638 SmallVector<int64_t> tileInterchange;
3640 state, transformOp, mixedInterchange, tileInterchange);
3644 SmallVector<Operation *> targets =
3646 SmallVector<SmallVector<Operation *>> dynamicSizeProducers;
3647 SmallVector<SmallVector<int64_t>> paramSizes;
3648 SmallVector<OpFoldResult> mixedTileSizes;
3649 if (hasPackedTiles) {
3651 state, transformOp, mixedTileSizes, getPackedTileSizes());
3659 if (isa<TransformParamTypeInterface>(transformValue.getType())) {
3660 dynamicSizeProducers.push_back({});
3661 ArrayRef<Attribute> params = state.
getParams(transformValue);
3662 paramSizes.push_back(llvm::map_to_vector(params, [](Attribute attr) {
3663 return cast<IntegerAttr>(attr).getValue().getSExtValue();
3666 if (paramSizes.back().size() != targets.size()) {
3667 DiagnosedSilenceableFailure
diag =
3668 emitSilenceableError()
3669 <<
"expected as many parameter values ("
3670 << dynamicSizeProducers.back().size() <<
") as target ops ("
3671 << targets.size() <<
")";
3672 diag.attachNote(transformValue.getLoc()) <<
"for this parameter";
3678 paramSizes.push_back({});
3679 dynamicSizeProducers.push_back(
3682 if (dynamicSizeProducers.back().size() != targets.size()) {
3683 DiagnosedSilenceableFailure
diag =
3684 emitSilenceableError()
3685 <<
"expected as many dynamic size-producing operations ("
3686 << dynamicSizeProducers.back().size() <<
") as target ops ("
3687 << targets.size() <<
")";
3688 diag.attachNote(transformValue.getLoc()) <<
"for this handle";
3692 for (Operation *op : dynamicSizeProducers.back()) {
3698 DiagnosedSilenceableFailure
diag =
3699 emitSilenceableError() <<
"expected sizes to be produced by ops "
3700 "with a single index-type result";
3701 diag.attachNote(op->
getLoc()) <<
"size producer op";
3702 diag.attachNote(transformValue.getLoc()) <<
"for this handle";
3708 SmallVector<Operation *> tiled;
3709 SmallVector<SmallVector<Operation *, 4>, 4> loops;
3712 ? llvm::count_if(mixedTileSizes,
3713 [](OpFoldResult ofr) {
3714 if (
auto attr = dyn_cast<Attribute>(ofr))
3715 return cast<IntegerAttr>(attr).getInt() != 0;
3718 : getLoops().size();
3719 loops.resize(numLoops);
3720 auto scalableSizes = getScalableSizes();
3721 for (
auto [i, op] : llvm::enumerate(targets)) {
3722 auto tilingInterface = dyn_cast<TilingInterface>(op);
3723 if (!tilingInterface) {
3724 DiagnosedSilenceableFailure
diag =
3725 emitSilenceableError()
3726 <<
"only ops implementing TilingInterface are supported";
3727 diag.attachNote(op->
getLoc()) <<
"target op";
3731 int64_t iterspaceRank = tilingInterface.getLoopIteratorTypes().size();
3732 if (tileInterchange.size() >
static_cast<size_t>(iterspaceRank)) {
3733 return emitSilenceableError()
3734 <<
"interchange length exceeds iteration space dimensions ("
3735 << iterspaceRank <<
")";
3737 SmallVector<bool> seen(iterspaceRank,
false);
3738 for (int64_t v : tileInterchange) {
3739 if (v < 0 || v >= iterspaceRank) {
3740 return emitSilenceableError()
3741 <<
"expects interchange values to be in range [0, "
3742 << iterspaceRank <<
"), found: " << v;
3745 return emitSilenceableError()
3746 <<
"found duplicate interchange value: " << v;
3751 if (tileSizes.size() > tilingInterface.getLoopIteratorTypes().size()) {
3752 DiagnosedSilenceableFailure
diag =
3753 emitSilenceableError()
3754 <<
"too many tiles provided, expected at most "
3755 << tilingInterface.getLoopIteratorTypes().size() <<
" found "
3756 << tileSizes.size();
3757 diag.attachNote(op->
getLoc()) <<
"target op";
3761 scf::SCFTilingOptions tilingOptions;
3762 if (!hasPackedTiles && tileSizes.empty()) {
3763 tilingOptions.setTileSizeComputationFunction(
3764 [](OpBuilder &, Operation *) -> SmallVector<OpFoldResult> {
3767 }
else if (hasPackedTiles) {
3768 tilingOptions.setTileSizes(mixedTileSizes);
3770 tilingOptions.setTileSizeComputationFunction([&, index = i](OpBuilder &
b,
3772 SmallVector<OpFoldResult> sizes;
3773 sizes.reserve(tileSizes.size());
3774 unsigned dynamicIdx = 0;
3776 for (
auto [ofrIdx, ofr] : llvm::enumerate(
getMixedSizes())) {
3777 if (
auto attr = llvm::dyn_cast_if_present<Attribute>(ofr)) {
3778 if (scalableSizes[ofrIdx]) {
3780 b, getLoc(), cast<IntegerAttr>(attr).getInt());
3782 vector::VectorScaleOp::create(
b, getLoc(),
b.getIndexType());
3784 arith::MulIOp::create(
b, getLoc(), val, vscale).getResult());
3786 sizes.push_back(attr);
3790 ArrayRef<Operation *> dynamicSizes = dynamicSizeProducers[dynamicIdx];
3791 ArrayRef<int64_t> params = paramSizes[dynamicIdx];
3793 assert((dynamicSizes.empty() ^ params.empty()) &&
3794 "expected either dynamic sizes or parameters");
3795 if (!params.empty()) {
3796 sizes.push_back(
b.getIndexAttr(params[index]));
3798 sizes.push_back(dynamicSizes[index]->getResult(0));
3805 tilingOptions.setInterchange(tileInterchange);
3806 tilingOptions.setInnerTileAlignments(
3808 FailureOr<scf::SCFTilingResult> maybeTilingResult =
3809 tileUsingSCF(rewriter, tilingInterface, tilingOptions);
3810 if (
failed(maybeTilingResult))
3813 rewriter.
replaceOp(op, maybeTilingResult->replacements);
3815 tiled.append(maybeTilingResult->tiledOps);
3816 for (
const auto &en2 : llvm::enumerate(maybeTilingResult->loops))
3817 loops[en2.index()].push_back(en2.value());
3820 transformResults.
set(cast<OpResult>(getTiledLinalgOp()), tiled);
3821 if (hasPackedTiles) {
3823 SmallVector<Operation *> flattenedLoops;
3824 for (
auto [targetIdx, _] : llvm::enumerate(targets))
3825 for (
auto [loopIdx, __] : llvm::enumerate(loops))
3826 flattenedLoops.push_back(loops[loopIdx][targetIdx]);
3827 transformResults.
set(cast<OpResult>(getLoops().front()), flattenedLoops);
3829 for (
const auto &en : llvm::enumerate(loops))
3830 transformResults.
set(cast<OpResult>(getLoops()[en.index()]), en.value());
3836SmallVector<OpFoldResult> transform::TileUsingForOp::getMixedSizes() {
3838 ArrayRef<int64_t> tileSizes = getStaticSizes();
3839 SmallVector<OpFoldResult> results;
3840 results.reserve(tileSizes.size());
3841 unsigned dynamicPos = 0;
3843 for (int64_t size : tileSizes) {
3844 if (size == ShapedType::kDynamic) {
3845 results.push_back(dynamic[dynamicPos++]);
3853SmallVector<OpFoldResult> transform::TileUsingForOp::getMixedInterchange() {
3857void transform::TileUsingForOp::getEffects(
3858 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3872void transform::TileUsingForallOp::build(OpBuilder &builder,
3874 ArrayRef<int64_t> staticTileSizes,
3875 transform::TileSizesSpec,
3877 return build(builder,
result,
3885void transform::TileUsingForallOp::build(OpBuilder &builder,
3887 ArrayRef<OpFoldResult> mixedTileSizes,
3888 transform::TileSizesSpec,
3890 SmallVector<int64_t> staticTileSizes;
3891 SmallVector<Value> dynamicTileSizes;
3897 auto operationType = transform::AnyOpType::get(ctx);
3900 TypeRange{operationType, operationType},
3907 staticTileSizesAttr,
3911void transform::TileUsingForallOp::build(OpBuilder &builder,
3913 ArrayRef<int64_t> staticNumThreads,
3914 transform::NumThreadsSpec,
3918 NumThreadsSpec(), mapping);
3921void transform::TileUsingForallOp::build(OpBuilder &builder,
3923 ArrayRef<OpFoldResult> mixedNumThreads,
3924 transform::NumThreadsSpec,
3926 SmallVector<int64_t> staticNumThreads;
3927 SmallVector<Value> dynamicNumThreads;
3934 auto operationType = transform::AnyOpType::get(ctx);
3937 TypeRange{operationType, operationType},
3943 staticNumThreadsAttr,
3950static SmallVector<OpFoldResult>
3956 AffineExpr normalizedUbExpr = (s1 - s0).ceilDiv(s2);
3958 for (
auto [lb,
ub, step] : llvm::zip_equal(lbs, ubs, steps)) {
3960 rewriter, loc, normalizedUbExpr, {lb,
ub, step});
3961 normalizedUbs.push_back(normalizedUb);
3963 return normalizedUbs;
3979 for (
auto [iv, lb, step] : llvm::zip_equal(ivs, lbs, steps)) {
3982 denormalizedIvs.push_back(
3985 return denormalizedIvs;
3996 scf::ForallOp loop) {
4013 auto normalizedForallOp = scf::ForallOp::create(
4014 rewriter, loc, normalizedLbs, normalizedUbs, normalizedSteps,
4015 loop.getOutputs(), loop.getMapping(),
4018 auto normalizedLoopIvs = normalizedForallOp.getInductionVars();
4020 Block *normalizedLoopBlock = normalizedForallOp.getBody();
4025 argValues.append(normalizedForallOp.getRegionIterArgs().begin(),
4026 normalizedForallOp.getRegionIterArgs().end());
4027 Block *origLoopBlock = loop.getBody();
4028 rewriter.
mergeBlocks(origLoopBlock, normalizedLoopBlock, argValues);
4030 rewriter.
replaceOp(loop, normalizedForallOp);
4031 return normalizedForallOp;
4039 scf::SCFTilingResult &tilingResult) {
4041 auto tileableOp = dyn_cast<TilingInterface>(
target);
4044 transformOp.emitSilenceableError()
4045 <<
"only TilingInterface ops are supported";
4046 diag.attachNote(
target->getLoc()) <<
"target op";
4050 scf::SCFTilingOptions
options;
4051 options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
4052 if (!mixedNumThreads.empty()) {
4053 options.setNumThreads(mixedNumThreads);
4055 options.setTileSizes(mixedTileSizes);
4058 options.setMapping(mapping.value().getValue());
4060 FailureOr<scf::SCFTilingResult> maybeTilingResult =
4061 scf::tileUsingSCF(rewriter, tileableOp,
options);
4063 if (failed(maybeTilingResult))
4064 return transformOp.emitDefaultSilenceableFailure(tileableOp);
4066 rewriter.
replaceOp(tileableOp, maybeTilingResult->replacements);
4068 tilingResult = *maybeTilingResult;
4072 if (mixedNumThreads.empty() && !tilingResult.loops.empty()) {
4073 auto generatedForallOp = cast<scf::ForallOp>(tilingResult.loops.front());
4076 scf::ForallOp normalizedForallOp =
4078 tilingResult.loops.front() = normalizedForallOp;
4088 auto transformOp = cast<TransformOpInterface>(getOperation());
4097 getPackedNumThreads()
4099 state, transformOp, mixedNumThreads, getPackedNumThreads())
4101 state, transformOp, mixedNumThreads, getMixedNumThreads());
4105 status = getPackedTileSizes()
4107 state, transformOp, mixedTileSizes, getPackedTileSizes())
4109 state, transformOp, mixedTileSizes, getMixedTileSizes());
4114 scf::SCFTilingResult tilingResult;
4116 rewriter, state, transformOp,
target, mixedNumThreads, mixedTileSizes,
4117 getMapping(), tilingResult);
4118 if (!
diag.succeeded())
4120 if (!tilingResult.loops.empty())
4121 tileOps.push_back(tilingResult.loops.front());
4122 tiledOps.append(tilingResult.tiledOps);
4125 transformResults.
set(cast<OpResult>(getForallOp()), tileOps);
4126 transformResults.
set(cast<OpResult>(getTiledOp()), tiledOps);
4131void transform::TileUsingForallOp::getEffects(
4132 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
4142SmallVector<OpFoldResult> TileUsingForallOp::getMixedNumThreads() {
4147SmallVector<OpFoldResult> TileUsingForallOp::getMixedTileSizes() {
4152LogicalResult TileUsingForallOp::verify() {
4153 int numThreadsSpec =
static_cast<int>(!getMixedNumThreads().empty()) +
4154 static_cast<int>(getPackedNumThreads() != Value());
4155 if (numThreadsSpec > 1)
4157 "num_threads and packed_num_threads are mutually exclusive");
4158 int tileSizesSpec =
static_cast<int>(!getMixedTileSizes().empty()) +
4159 static_cast<int>(getPackedTileSizes() != Value());
4160 if (tileSizesSpec > 1)
4162 "tile_sizes and packed_tile_sizes are mutually exclusive");
4163 if (numThreadsSpec == 0 && tileSizesSpec == 0)
4164 return emitOpError(
"either (packed_)num_threads or (packed_)tile_sizes "
4165 "must be specified");
4173void transform::VectorizeChildrenAndApplyPatternsOp::build(
4174 OpBuilder &builder, OperationState &
result, Value
target,
4175 bool foldTypeExtensionsIntoContract,
bool vectorizePadding,
4176 bool vectorizeExtract,
bool flatten1DDepthwiseConv) {
4178 if (foldTypeExtensionsIntoContract) {
4180 VectorizeChildrenAndApplyPatternsOp::
4181 getFoldTypeExtensionsIntoContractAttrName(
result.name),
4184 if (vectorizePadding) {
4186 VectorizeChildrenAndApplyPatternsOp::getVectorizePaddingAttrName(
4190 if (vectorizeExtract) {
4192 VectorizeChildrenAndApplyPatternsOp::getVectorizeNdExtractAttrName(
4196 if (flatten1DDepthwiseConv) {
4198 VectorizeChildrenAndApplyPatternsOp::getFlatten_1dDepthwiseConvAttrName(
4208struct VectorizationPattern :
public RewritePattern {
4209 explicit VectorizationPattern(MLIRContext *context,
4210 bool vectorizeExtract =
false,
4211 bool flattenConv =
false)
4212 : RewritePattern(MatchAnyOpTypeTag(), 1, context),
4213 vectorizeNDExtract(vectorizeExtract),
4214 flatten1DDepthwiseConv(flattenConv) {}
4215 LogicalResult matchAndRewrite(Operation *op,
4216 PatternRewriter &rewriter)
const override {
4219 "Unsupported Op, cannot vectorize");
4220 FailureOr<VectorizationResult> vectorResults =
4222 {}, vectorizeNDExtract,
4223 flatten1DDepthwiseConv);
4224 if (
failed(vectorResults))
4226 rewriter.
replaceOp(op, vectorResults->replacements);
4233 bool vectorizeNDExtract =
false;
4237 bool flatten1DDepthwiseConv =
false;
4241DiagnosedSilenceableFailure
4242transform::VectorizeChildrenAndApplyPatternsOp::applyToOne(
4243 transform::TransformRewriter &rewriter, Operation *
target,
4244 transform::ApplyToEachResultList &results,
4245 transform::TransformState &state) {
4246 if (!
target->hasTrait<OpTrait::IsIsolatedFromAbove>()) {
4247 auto diag = this->emitOpError(
"requires isolated-from-above targets");
4248 diag.attachNote(
target->getLoc()) <<
"non-isolated target";
4253 RewritePatternSet patterns(ctx);
4254 patterns.
add<VectorizationPattern>(ctx, getVectorizeNdExtract(),
4255 getFlatten_1dDepthwiseConv());
4257 if (!getDisableTransferPermutationMapLoweringPatterns())
4260 if (!getDisableMultiReductionToContractPatterns())
4265 patterns.
add<linalg::LinalgCopyVTRForwardingPattern,
4266 linalg::LinalgCopyVTWForwardingPattern>(ctx,
4268 vector::TransferReadOp::getCanonicalizationPatterns(patterns, ctx);
4269 vector::TransferWriteOp::getCanonicalizationPatterns(patterns, ctx);
4272 patterns.
add<CopyVectorizationPattern>(ctx);
4274 if (getFoldTypeExtensionsIntoContract())
4277 if (getVectorizePadding()) {
4285 TrackingListener listener(state, *
this);
4288 GreedyRewriteConfig().setListener(&listener))))
4289 return emitDefaultDefiniteFailure(
target);
4299DiagnosedSilenceableFailure transform::VectorizeOp::apply(
4300 transform::TransformRewriter &rewriter,
4301 mlir::transform::TransformResults &transformResults,
4302 mlir::transform::TransformState &state) {
4304 if (std::empty(targets))
4306 auto transformOp = cast<TransformOpInterface>(getOperation());
4307 SmallVector<int64_t> vectorSizes;
4309 state, transformOp, getMixedVectorSizes(), vectorSizes);
4314 for (Operation *
target : targets) {
4317 <<
"Unsupported Op, cannot vectorize";
4319 FailureOr<VectorizationResult> vectorResults =
4321 getVectorizeNdExtract().value_or(
false),
4323 getAssumeDynamicDimsMatchVecSizes().value_or(
false),
4324 getCreateNamedContraction().value_or(
false));
4325 if (
failed(vectorResults)) {
4327 <<
"Attempted to vectorize, but failed";
4335void transform::VectorizeOp::getEffects(
4336 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
4342SmallVector<OpFoldResult> VectorizeOp::getMixedVectorSizes() {
4347LogicalResult transform::VectorizeOp::verify() {
4348 if (getStaticVectorSizes().size() != getScalableSizes().size())
4349 return emitOpError(
"expected same number of vector sizes (")
4350 << getStaticVectorSizes().size() <<
") and scalable sizes ("
4351 << getScalableSizes().size() <<
")";
4359DiagnosedSilenceableFailure
4360transform::HoistRedundantVectorTransfersOp::applyToOne(
4361 transform::TransformRewriter &rewriter, func::FuncOp
target,
4362 transform::ApplyToEachResultList &results,
4363 transform::TransformState &state) {
4376DiagnosedSilenceableFailure
4377transform::HoistRedundantVectorBroadcastsOp::applyToOne(
4378 transform::TransformRewriter &rewriter, mlir::Operation *
target,
4379 transform::ApplyToEachResultList &results,
4380 transform::TransformState &state) {
4391DiagnosedSilenceableFailure transform::ConvertConv2DToImg2ColOp::applyToOne(
4392 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4393 transform::ApplyToEachResultList &results,
4394 transform::TransformState &state) {
4396 auto maybeTransformed =
4399 .Case([&](linalg::Conv2DNhwcHwcfOp op) {
4402 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4405 .Case([&](linalg::DepthwiseConv2DNhwcHwcOp op) {
4408 .Case([&](linalg::Conv2DNchwFchwOp op) {
4411 .Default([&](Operation *op) {
4414 if (
failed(maybeTransformed))
4415 return emitDefaultSilenceableFailure(
target);
4417 results.
push_back(maybeTransformed->first);
4419 results.
push_back(maybeTransformed->second);
4427DiagnosedSilenceableFailure transform::FlattenElementwiseLinalgOp::applyToOne(
4428 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4429 transform::ApplyToEachResultList &results,
4430 transform::TransformState &state) {
4434 <<
"only elementwise flattening is supported";
4436 if (!llvm::all_of(
target.getIndexingMapsArray(), [](AffineMap m) {
4437 return m.isPermutation() || m.getNumResults() == 0;
4441 <<
"broadcasting of non scalar operands is not supported";
4445 if (
target.getNumLoops() <= 1) {
4452 if (
auto broadcastOp = dyn_cast<linalg::BroadcastOp>(
target.getOperation());
4453 broadcastOp && broadcastOp.getInput().getType().getRank() != 0) {
4460 std::iota(reassociation.begin(), reassociation.end(), 0);
4461 auto maybeFlattened =
4463 if (
failed(maybeFlattened))
4465 <<
"attempted to flatten, but failed";
4466 results.
push_back(maybeFlattened->collapsedOp);
4475DiagnosedSilenceableFailure transform::TransposeConv2DOp::applyToOne(
4476 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4477 transform::ApplyToEachResultList &results,
4478 transform::TransformState &state) {
4480 auto maybeTransformed =
4482 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4485 .Case([&](linalg::Conv2DNhwcFhwcQOp op) {
4488 .Default([&](Operation *op) {
4491 if (
failed(maybeTransformed))
4492 return emitDefaultSilenceableFailure(
target);
4502DiagnosedSilenceableFailure transform::TransposeMatmulOp::applyToOne(
4503 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4504 transform::ApplyToEachResultList &results,
4505 transform::TransformState &state) {
4507 bool transposeLHS = getInputToTranspose() == TransposeMatmulInput::lhs;
4508 auto maybeTransformed =
4510 .Case([&](linalg::MatmulOp op) {
4513 .Case([&](linalg::BatchMatmulOp op) {
4516 .Default(failure());
4517 if (
failed(maybeTransformed))
4527template <
typename OpTy>
4528static DiagnosedSilenceableFailure
4532 static_assert(llvm::is_one_of<OpTy, tensor::InsertSliceOp,
4533 tensor::ParallelInsertSliceOp>() &&
4536 if (
auto copySource =
4537 target.getSource().template getDefiningOp<linalg::CopyOp>()) {
4545 if (isa<mlir::ParallelCombiningOpInterface>(
target.getOperation()))
4548 Value extracted = tensor::ExtractSliceOp::create(
4551 Value copied = linalg::CopyOp::create(rewriter,
target.getLoc(),
4552 target.getSource(), extracted)
4564DiagnosedSilenceableFailure transform::InsertSliceToCopyOp::applyToOne(
4565 transform::TransformRewriter &rewriter, Operation *targetOp,
4566 transform::ApplyToEachResultList &results,
4567 transform::TransformState &state) {
4570 if (
auto target = dyn_cast<tensor::InsertSliceOp>(targetOp))
4571 return doit(rewriter,
target, results, state);
4572 if (
auto target = dyn_cast<tensor::ParallelInsertSliceOp>(targetOp))
4573 return doit(rewriter,
target, results, state);
4575 DiagnosedSilenceableFailure
diag =
4576 emitSilenceableError()
4577 <<
"only InsertSliceOp and ParallelInsertSliceOp ops are supported";
4578 diag.attachNote(targetOp->
getLoc()) <<
"target op";
4586DiagnosedSilenceableFailure transform::MapCopyToThreadsOp::applyToOne(
4587 transform::TransformRewriter &rewriter, Operation *
target,
4588 transform::ApplyToEachResultList &results,
4589 transform::TransformState &state) {
4591 if (!isa<linalg::CopyOp, tensor::PadOp>(
target)) {
4592 DiagnosedSilenceableFailure
diag =
4593 emitSilenceableError()
4594 <<
"only linalg.copy and tensor.pad target ops are supported";
4595 diag.attachNote(
target->getLoc()) <<
"target op";
4598 assert(
target->getNumResults() == 1 &&
"expected single result");
4599 auto resultShapedType = cast<ShapedType>(
target->getResult(0).getType());
4600 if (!resultShapedType.hasStaticShape()) {
4601 DiagnosedSilenceableFailure
diag =
4602 emitSilenceableError()
4603 <<
"only statically sized ops of rank <= 3 are supported";
4604 diag.attachNote(
target->getLoc()) <<
"target op";
4609 int64_t desiredBitAlignment = getDesiredBitAlignment();
4610 int64_t eltBitwidth =
4611 resultShapedType.getElementType().getIntOrFloatBitWidth();
4612 if (desiredBitAlignment % eltBitwidth != 0) {
4613 desiredBitAlignment = eltBitwidth;
4616 gpu::CopyMappingInfo mapping(
4618 getTotalNumThreads(),
4619 desiredBitAlignment,
4620 resultShapedType.getShape(),
4623 resultShapedType.getElementType().getIntOrFloatBitWidth());
4624 if (mapping.status == gpu::CopyMappingInfo::Status::Invalid) {
4625 DiagnosedSilenceableFailure
diag =
4626 emitSilenceableError()
4627 <<
"too few threads to map copy op to threads on the most minor "
4628 "dimension, given alignment and vector size constraints, try "
4629 "smaller tile size of mapping to more threads";
4630 diag.attachNote(
target->getLoc()) <<
"target op";
4636 scf::SCFTilingResult tilingResult;
4643 ArrayRef<OpFoldResult>{},
4644 b.getArrayAttr(mapping.threadMapping),
4646 if (!
diag.succeeded())
4649 results.
push_back(tilingResult.loops.front());
4650 for (
auto *op : tilingResult.tiledOps)
4659DiagnosedSilenceableFailure transform::WinogradConv2DOp::applyToOne(
4660 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4661 transform::ApplyToEachResultList &results,
4662 transform::TransformState &state) {
4664 FailureOr<Operation *> maybeTransformed = failure();
4666 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4671 .Default([&](Operation *op) {
return false; });
4674 return emitSilenceableError()
4675 <<
"this operation is not supported to convert to Winograd Conv2D";
4678 if (
failed(maybeTransformed)) {
4679 return emitSilenceableError() <<
"apply Winograd Conv2D failed";
4686DiagnosedSilenceableFailure transform::DecomposeWinogradOp::applyToOne(
4687 transform::TransformRewriter &rewriter, Operation *
target,
4688 transform::ApplyToEachResultList &results,
4689 transform::TransformState &state) {
4691 FailureOr<Operation *> maybeTransformed = failure();
4694 .Case([&](linalg::WinogradFilterTransformOp op) {
4698 .Case([&](linalg::WinogradInputTransformOp op) {
4702 .Case([&](linalg::WinogradOutputTransformOp op) {
4709 DiagnosedSilenceableFailure
diag =
4710 emitSilenceableError()
4711 <<
"this operation is not supported to decompose into other operations";
4712 diag.attachNote(
target->getLoc()) <<
"target op";
4716 if (
failed(maybeTransformed)) {
4717 DiagnosedSilenceableFailure
diag =
4718 emitSilenceableError() <<
"decompose Winograd operations failed";
4719 diag.attachNote(
target->getLoc()) <<
"target op";
4727#include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOpsEnums.cpp.inc"
4729#define GET_OP_CLASSES
4730#include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp.inc"
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static std::string diag(const llvm::Value &value)
memberIdxs push_back(ArrayAttr::get(parser.getContext(), values))
static llvm::ManagedStatic< PassManagerOptions > options
static void getDynamicSizes(RankedTensorType tp, ValueRange sizes, SmallVectorImpl< Value > &dynSizes)
Collects the dynamic dimension sizes for tp with the assumption that sizes are the dimension sizes fo...
static SmallVector< Value > getTileSizes(Location loc, x86::amx::TileType tType, RewriterBase &rewriter)
Maps the 2-dim vector shape to the two 16-bit tile sizes.
Base type for affine expression.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
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 parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
Attributes are known-constant values of operations.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getIntegerAttr(Type type, int64_t value)
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
AffineExpr getAffineSymbolExpr(unsigned position)
IntegerAttr getI64IntegerAttr(int64_t value)
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
MLIRContext * getContext() const
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
ArrayAttr getStrArrayAttr(ArrayRef< StringRef > values)
Diagnostic & attachNote(std::optional< Location > loc=std::nullopt)
Attaches a note to the error.
The result of a transform IR operation application.
static DiagnosedSilenceableFailure success()
Constructs a DiagnosedSilenceableFailure in the success state.
bool isDefiniteFailure() const
Returns true if this is a definite failure.
static DiagnosedSilenceableFailure silenceableFailure(Diagnostic &&diag)
Constructs a DiagnosedSilenceableFailure in the silenceable failure state, ready to emit the given di...
bool succeeded() const
Returns true if this is a success.
static DiagnosedSilenceableFailure definiteFailure()
Constructs a DiagnosedSilenceableFailure in the failure state.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
A class for computing basic dominance information.
bool dominates(Operation *a, Operation *b) const
Return true if operation A dominates operation B, i.e.
This is a utility class for mapping one set of IR entities to another.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
IRValueT get() const
Return the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
NamedAttribute represents a combination of a name and an Attribute value.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
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 OptionalParseResult parseOptionalOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single operand if present.
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.
void printFunctionalType(Operation *op)
Print the complete type of an operation in functional form.
bool isSet() const
Returns true if this insert point is set.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setListener(Listener *newListener)
Sets the listener of this builder to the one provided.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Listener * getListener() const
Returns the current listener of this builder, or nullptr if this builder doesn't have a listener.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents a single result from folding an operation.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
This is a value defined by a result of an operation.
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
Attribute getDiscardableAttr(StringRef name)
Access a discardable attribute by name, returns a null Attribute if the discardable attribute does no...
OpResult getOpResult(unsigned idx)
void setOperand(unsigned idx, Value value)
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
std::optional< Attribute > getInherentAttr(StringRef name)
Access an inherent attribute by name: returns an empty optional if there is no inherent attribute wit...
Location getLoc()
The source location the operation was defined or derived from.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
operand_type_range getOperandTypes()
result_type_range getResultTypes()
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
user_range getUsers()
Returns a range of all users.
result_range getOpResults()
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
unsigned getNumResults()
Return the number of results held by this operation.
bool has_value() const
Returns true if we contain a valid ParseResult value.
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void eraseBlock(Block *block)
This method erases all operations in a block.
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.
virtual void replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
void replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class 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.
Type front()
Return first type in the range.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
bool use_empty() const
Returns true if this value has no uses.
Type getType() const
Return the type of this value.
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
user_range getUsers() const
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
State for analysis-enabled bufferization.
Operation * getOwner() const
Return the owner of this operand.
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Returns a composed AffineApplyOp by composing map and operands with other AffineApplyOps supplying th...
SmallVector< OpFoldResult > makeComposedFoldedMultiResultAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Variant of makeComposedFoldedAffineApply suitable for multi-result maps.
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
LogicalResult analyzeOp(Operation *op, OneShotAnalysisState &state, BufferizationStatistics *statistics=nullptr)
Analyze op and its nested ops.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
FailureOr< PackingResult > buildPackingLoopNest(RewriterBase &rewriter, tensor::PadOp opToHoist, scf::ForOp outermostEnclosingForOp, ArrayRef< int64_t > transposeVector)
Build the packing loop nest required to hoist opToHoist above outermostEnclosingForOp.
void populateDataLayoutPropagationPatterns(RewritePatternSet &patterns, const ControlPropagationFn &controlPackUnPackPropagation, bool PoisonPaddingOk=false)
Patterns to bubble up or down data layout ops across other operations.
LogicalResult rewriteAsPaddedOp(RewriterBase &rewriter, LinalgOp opToPad, const LinalgPaddingOptions &options, LinalgOp &paddedOp, SmallVector< Value > &replacements, SmallVector< tensor::PadOp > &padOps)
Pad the iterator dimensions options.paddingDimensions of all opToPad operands to a static bounding bo...
FailureOr< std::pair< Operation *, Operation * > > rewriteInIm2Col(RewriterBase &rewriter, linalg::Conv2DNhwcHwcfOp convOp)
Convert linalg.conv_2d_nhwc_hwcf into linalg.generic (for img2col packing) and linalg....
bool hasVectorizationImpl(Operation *)
Return true if there's dedicated logic in the Linalg Vectorizer to vectorize this Op,...
void populateExtractSliceSinkingPatterns(RewritePatternSet &patterns, const ControlPropagationFn &controlPackUnPackPropagation)
Patterns to sink extract slice across other operations.
FailureOr< Operation * > decomposeWinogradFilterTransformOp(RewriterBase &rewriter, linalg::WinogradFilterTransformOp op)
Rewrite linalg.winograd_filter_transform.
std::optional< Value > allocateWorkgroupMemory(OpBuilder &builder, memref::SubViewOp subview, ArrayRef< Value > sizeBounds, DataLayout &)
Allocate the subview in the GPU workgroup memory.
FailureOr< PackTransposeResult > packTranspose(RewriterBase &rewriter, linalg::PackOp packOp, linalg::LinalgOp linalgOp, linalg::UnPackOp maybeUnPackOp, ArrayRef< int64_t > outerPerm, ArrayRef< int64_t > innerPerm)
Transpose a single PackOp -> LinalgOp -> UnPackOp chain and return the transposed PackOp -> LinalgOp ...
Value bufferizeToAllocation(RewriterBase &rewriter, const BufferizeToAllocationOptions &options, tensor::PadOp padOp, Attribute memorySpace={}, Operation *insertionPoint=nullptr)
Materialize a buffer allocation for the given tensor.pad op and lower the op to linalg....
FailureOr< VectorizationResult > vectorize(RewriterBase &rewriter, Operation *op, ArrayRef< int64_t > inputVectorSizes={}, ArrayRef< bool > inputScalableVecDims={}, bool vectorizeNDExtract=false, bool flatten1DDepthwiseConv=false, bool assumeDynamicDimsMatchVecSizes=false, bool createNamedContraction=false)
Returns a VectorizationResult containing the results of the vectorized op, or failure if the transfor...
FailureOr< Value > hoistPaddingOnTensors(RewriterBase &rewriter, tensor::PadOp opToHoist, int64_t numLoops, ArrayRef< int64_t > transposeVector, tensor::PadOp &hoistedOp, SmallVectorImpl< TransposeOp > &transposeOps)
Mechanically hoist padding operations on tensors by numLoops into a new, generally larger tensor.
FailureOr< LowerUnPackOpResult > lowerUnPack(RewriterBase &rewriter, linalg::UnPackOp unPackOp, bool lowerUnpadLikeWithExtractSlice=true)
Rewrite pack as empty + transpose + reshape + extract_slice + copy.
void populatePadOpVectorizationPatterns(RewritePatternSet &patterns, PatternBenefit baseBenefit=1)
Populates patterns with patterns that vectorize tensor.pad.
void populateLinalgTilingCanonicalizationPatterns(RewritePatternSet &patterns)
Canonicalization patterns relevant to apply after tiling patterns.
LogicalResult deallocateGPUPrivateMemory(OpBuilder &, Value)
In case of GPU private memory there is no need to deallocate since the memory is freed when going out...
FailureOr< Operation * > decomposeWinogradOutputTransformOp(RewriterBase &rewriter, linalg::WinogradOutputTransformOp op)
Rewrite linalg.winograd_output_transform.
std::function< SplitReductionOptions(LinalgOp op)> ControlSplitReductionFn
Function signature to control reduction splitting.
std::optional< Value > allocateGPUPrivateMemory(OpBuilder &builder, memref::SubViewOp subview, ArrayRef< Value > sizeBounds, DataLayout &)
Allocate the subview in the GPU private memory.
FailureOr< Operation * > rewriteInDestinationPassingStyle(RewriterBase &rewriter, tensor::FromElementsOp fromElementsOp)
Rewrite tensor.from_elements to linalg.generic.
FailureOr< Operation * > winogradConv2D(RewriterBase &rewriter, linalg::Conv2DNhwcFhwcOp op, WinogradConv2DFmr fmr)
Convert linalg.conv_2d_nhwc_fhwc to Winograd Conv2D algorithm F(m x m, r x r).
FailureOr< Operation * > transposeConv2D(RewriterBase &rewriter, linalg::Conv2DNhwcFhwcOp op)
Convert linalg.conv_2d_nhwc_fhwc(_q) to linalg.conv_2d_nhwc_hwcf(_q) by materializing transpose.
void populateFoldUnitExtentDimsPatterns(RewritePatternSet &patterns, ControlDropUnitDims &options)
Patterns to fold unit-extent dimensions in operands/results of linalg ops on tensors and memref.
LogicalResult copyToWorkgroupMemory(OpBuilder &b, Value src, Value dst)
Create Memref copy operations and add gpu barrier guards before and after the copy operation to ensur...
FailureOr< LinalgOp > generalizeNamedOp(RewriterBase &rewriter, LinalgOp linalgOp, bool emitCategoryOps=false)
Create a GenericOp or CategoryOp from the given named operation linalgOp and replace the given linalg...
LogicalResult linalgOpAnchoredEmptyTensorEliminationStep(RewriterBase &rewriter, Operation *op, bufferization::OneShotAnalysisState &state)
Try to eliminate tensor::EmptyOps inside op that are anchored on a LinalgOp.
FailureOr< Operation * > transposeBatchMatmul(RewriterBase &rewriter, linalg::BatchMatmulOp op, bool transposeLHS=true)
Pattern to replace.
FailureOr< LinalgOp > specializeGenericOp(RewriterBase &rewriter, GenericOp genericOp, bool emitCategoryOps=false)
Replace the given GenericOp with a namedOp or categoryOp.
LogicalResult promoteSubviewsPrecondition(Operation *op, LinalgPromotionOptions options)
Promote memref.subviews feeding linalg-on-buffers operations.
LogicalResult copyToGPUPrivateMemory(OpBuilder &b, Value src, Value dst)
Normal copy to between src and dst.
bool isElementwise(LinalgOp op)
Check if a LinalgOp is an element-wise operation.
FailureOr< GenericOp > interchangeGenericOp(RewriterBase &rewriter, GenericOp genericOp, ArrayRef< unsigned > interchangeVector)
Interchange the iterator_types and iterator_maps dimensions and adapts the index accesses of op.
FailureOr< StaticMultiSizeSpecification > computeStaticMultiTileSizes(LinalgOp op, unsigned dimension, int64_t targetSize, int64_t divisor)
void populateDecomposePackUnpackPatterns(RewritePatternSet &patterns)
Populates patterns to decompose linalg.pack and linalg.unpack Ops into e.g.
FailureOr< ContinuousTileSizeSpecification > computeContinuousTileSizes(OpBuilder &builder, TilingInterface op, unsigned dimension, OpFoldResult targetSize, bool emitAssertions)
FailureOr< StaticContinuousTileSizeSpecification > computeStaticContinuousTileSizes(LinalgOp op, unsigned dimension, unsigned targetSize)
FailureOr< SplitReductionResult > splitReduction(RewriterBase &b, LinalgOp op, const ControlSplitReductionFn &controlSplitReductionFn, bool useAlloc=false)
void populateFoldPackUnpackIntoTensorEmptyPatterns(RewritePatternSet &patterns)
Populates patterns with patterns that fold operations like linalg.pack and linalg....
void populateFoldIntoPackAndUnpackPatterns(RewritePatternSet &patterns, const ControlFoldIntoPackUnpackFn &controlFn=nullptr)
Populates patterns with patterns that fold operations like tensor.pad and tensor.extract_slice into t...
void hoistRedundantVectorBroadcasts(RewriterBase &rewriter, Operation *root)
Hoist vector.extract/vector.broadcast pairs out of immediately enclosing scf::ForOp iteratively,...
FailureOr< PackResult > packMatmulGreedily(RewriterBase &rewriter, LinalgOp linalgOp, ArrayRef< OpFoldResult > mnkPackedSizes, ArrayRef< int64_t > mnkPaddedSizesNextMultipleOf, ArrayRef< int64_t > mnkOrder)
Pack a LinalgOp by greedily inferring matmul dimensions (m, n, k) where m and n are proper parallel d...
FailureOr< PackResult > pack(RewriterBase &rewriter, linalg::LinalgOp linalgOp, ArrayRef< OpFoldResult > packedSizes)
Implement packing of a single LinalgOp by packedSizes.
void populateEraseUnnecessaryInputsPatterns(RewritePatternSet &patterns)
Patterns to promote inputs to outputs and remove unused inputs of linalg.generic ops.
std::function< bool(OpOperand *opOperand)> ControlPropagationFn
Function type which is used to control propagation of linalg.pack/unpack ops.
void populateSwapExtractSliceWithFillPatterns(RewritePatternSet &patterns)
Adds patterns that waps tensor.extract_slice(linalg.fill(cst, init)) into linalg.fill(cst,...
FailureOr< LinalgOp > promoteSubViews(OpBuilder &b, LinalgOp op, const LinalgPromotionOptions &options)
Promote the subViews into a new buffer allocated at the insertion point b.
LogicalResult deallocateWorkgroupMemory(OpBuilder &, Value)
In case of GPU group memory there is no need to deallocate.
FailureOr< Operation * > transposeMatmul(RewriterBase &rewriter, linalg::MatmulOp op, bool transposeLHS=true)
Convert Linalg matmul ops to transposed variants.
FailureOr< CollapseResult > collapseOpIterationDims(LinalgOp op, ArrayRef< ReassociationIndices > foldedIterationDims, RewriterBase &rewriter)
Collapses dimensions of linalg.generic/linalg.copy operation.
void hoistRedundantVectorTransfers(Operation *root, bool verifyNonZeroTrip=false)
Hoist vector.transfer_read/vector.transfer_write on buffers pairs out of immediately enclosing scf::F...
FailureOr< Operation * > decomposeWinogradInputTransformOp(RewriterBase &rewriter, linalg::WinogradInputTransformOp op)
Rewrite linalg.winograd_input_transform.
void populateDecomposePadPatterns(RewritePatternSet &patterns)
Populates patterns to decompose tensor.pad into e.g.
void populateFoldAddIntoDestPatterns(RewritePatternSet &patterns)
Pattern to replace linalg.add when destination passing on a contraction op suffices for achieving the...
std::pair< TilingInterface, TilingInterface > splitOp(RewriterBase &rewriter, TilingInterface op, unsigned dimension, OpFoldResult splitPoint)
Split the given op into two parts along the given iteration space dimension at the specified splitPoi...
FailureOr< SplitReductionResult > splitReductionByScaling(RewriterBase &b, LinalgOp op, const ControlSplitReductionFn &controlSplitReductionFn, bool useAlloc=false)
Scaling-based implementation of the split reduction transformation.
FailureOr< MultiSizeSpecification > computeMultiTileSizes(OpBuilder &builder, LinalgOp op, unsigned dimension, OpFoldResult targetSize, OpFoldResult divisor, bool emitAssertions=true)
Emits the IR computing the multi-sized tiling specification with two tile sizes not exceeding targetS...
FailureOr< LinalgOp > downscaleSizeOneWindowedConvolution(RewriterBase &rewriter, LinalgOp op)
Rewrite convolution/pooling/depthwise ops with size-1 window dimensions into lower-dimensional ops.
FailureOr< LowerPackResult > lowerPack(RewriterBase &rewriter, linalg::PackOp packOp, bool lowerPadLikeWithInsertSlice=true)
Rewrite pack as pad + reshape + transpose.
ForOp getForInductionVarOwner(Value val)
Returns the loop parent of an induction variable.
void populateMergeConsecutiveInsertExtractSlicePatterns(RewritePatternSet &patterns)
Collects patterns to merge consecutive tensor.insert_slice/extract_slice into one.
void populateBubbleUpExtractSliceOpPatterns(RewritePatternSet &patterns)
Appends patterns that are used to bubble up tensor.extract slice op above its producer.
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given tensor value.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
LogicalResult getOrCreateDestinations(OpBuilder &b, Location loc, Operation *op, SmallVector< Value > &result)
This is a helper function for DestinationStyleOpInterface.
void populateFoldTensorSubsetIntoVectorTransferPatterns(RewritePatternSet &patterns)
Appends patterns for folding tensor subset ops into vector transfer ops.
detail::poison_attr_matcher m_Poison()
Matches a poison constant (any attribute implementing PoisonAttrInterface).
void populateVectorTransferPermutationMapLoweringPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect a set of transfer read/write lowering patterns that simplify the permutation map (e....
void populateFoldArithExtensionPatterns(RewritePatternSet &patterns)
Collect a set of patterns that fold arithmetic extension on floating point into vector contract for t...
void populateSinkVectorOpsPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Patterns that remove redundant Vector Ops by re-ordering them with e.g.
void populateVectorReductionToContractPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect patterns to convert reduction op to vector.contract and fold transpose/broadcast ops into the...
void populateVectorStepLoweringPatterns(RewritePatternSet &patterns, unsigned indexBitwidth=64, PatternBenefit benefit=1)
Populate the pattern set with the following patterns:
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
@ PartialReductionOuterReduction
@ PartialReductionOuterParallel
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
SmallVector< InnerTileAlignment > convertInnerTileAlignments(ArrayRef< int64_t > alignments)
Maps a validated inner_tile_alignments integer array onto the per-dimension InnerTileAlignment hints ...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
DiagnosedSilenceableFailure emitSilenceableFailure(Location loc, const Twine &message={})
Emits a silenceable failure with the given message.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Attribute parseAttribute(llvm::StringRef attrStr, MLIRContext *context, Type type={}, size_t *numRead=nullptr, bool isKnownNullTerminated=false)
This parses a single MLIR attribute to an MLIR context if it was valid.
llvm::SetVector< T, Vector, Set, N > SetVector
DiagnosedDefiniteFailure emitDefiniteFailure(Location loc, const Twine &message={})
Emits a definite failure with the given message.
LogicalResult verifyInnerTileAlignments(Operation *op, ArrayRef< int64_t > alignments)
Verifies that every entry of a raw inner_tile_alignments integer array is a valid InnerTileAlignment,...
FailureOr< SCFTileAndFuseResult > tileConsumerAndFuseProducersUsingSCF(RewriterBase &rewriter, TilingInterface consumer, const SCFTileAndFuseOptions &options)
Method to tile and fuse a sequence of operations, by tiling the consumer and fusing its producers.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
llvm::TypeSwitch< T, ResultT > TypeSwitch
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
SmallVector< int64_t, 2 > ReassociationIndices
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
SmallVector< IntTy > extractFromIntegerArrayAttr(Attribute attr)
Extract integer values from the assumed ArrayAttr of IntegerAttr.
llvm::function_ref< Fn > function_ref
bool isPermutationVector(ArrayRef< int64_t > interchange)
Method to check if an interchange vector is a permutation.
bool isOneInteger(OpFoldResult v)
Return true if v is an IntegerAttr with value 1.
FailureOr< SCFTilingResult > tileUsingSCF(RewriterBase &rewriter, TilingInterface op, const SCFTilingOptions &options)
Method to tile an op that implements the TilingInterface using scf.for for iterating over the tiles.
This class represents a listener that may be used to hook into various actions within an OpBuilder.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Represents a range (offset, size, and stride) where each element of the triple may be dynamic or stat...
A listener that forwards all notifications to another listener.
ForwardingListener(OpBuilder::Listener *listener)
Container for result values of tiling.
SmallVector< Value > tiledValues
Options for analysis-enabled bufferization.
Transformation to drop unit-extent dimensions from linalg.generic operations.