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(
274void transform::ApplyEraseUnusedOperandsAndResultsPatternsOp::populatePatterns(
279void transform::ApplyFoldReshapeOpsByExpansionPatternsOp::populatePatterns(
282 [foldSingleUseOnly = getFoldSingleUseOnly()](
OpOperand *fusedOperand) {
283 if (!foldSingleUseOnly)
285 Operation *producer = fusedOperand->get().getDefiningOp();
286 return producer && producer->
hasOneUse();
300 SmallVector<Operation *> getNewOps()
const {
301 return SmallVector<Operation *>(newOps.begin(), newOps.end());
305 void notifyOperationInserted(Operation *op,
306 OpBuilder::InsertPoint previous)
override {
307 ForwardingListener::notifyOperationInserted(op, previous);
309 if (previous.
isSet())
313 assert(
inserted.second &&
"expected newly created op");
316 void notifyOperationErased(Operation *op)
override {
317 ForwardingListener::notifyOperationErased(op);
318 op->
walk([&](Operation *op) { newOps.erase(op); });
330 llvm::scope_exit resetListener(
331 [&]() { rewriter.
setListener(previousListener); });
332 NewOpsListener newOpsListener(previousListener);
336 if (getMemcpyOp() ==
"bufferization.materialize_in_destination") {
337 options.memcpyOp = linalg::BufferizeToAllocationOptions::MemcpyOp::
338 MaterializeInDestination;
339 }
else if (getMemcpyOp() ==
"memref.copy") {
342 }
else if (getMemcpyOp() ==
"linalg.copy") {
346 llvm_unreachable(
"invalid memcpy op");
348 if (getAllocOp() ==
"memref.alloc") {
351 }
else if (getAllocOp() ==
"memref.alloca") {
355 llvm_unreachable(
"invalid alloc op");
357 options.bufferizeDestinationOnly = getBufferizeDestinationOnly();
358 options.emitDealloc = getEmitDealloc();
362 getMemorySpace().has_value() ? getMemorySpace().value() :
Attribute();
369 <<
"failed to bufferize operation";
370 diag.attachNote(op->
getLoc()) <<
"target payload op";
373 allocatedBuffers.push_back(buffer);
377 results.
setValues(cast<OpResult>(getAllocatedBuffer()), allocatedBuffers);
378 results.
set(cast<OpResult>(getNewOps()), newOpsListener.getNewOps());
382void transform::BufferizeToAllocationOp::getEffects(
384 if (getBufferizeDestinationOnly()) {
395LogicalResult transform::BufferizeToAllocationOp::verify() {
396 if (getMemcpyOp() !=
"bufferization.materialize_in_destination" &&
397 getMemcpyOp() !=
"memref.copy" && getMemcpyOp() !=
"linalg.copy")
398 return emitOpError() <<
"unsupported memcpy op";
399 if (getAllocOp() !=
"memref.alloc" && getAllocOp() !=
"memref.alloca")
400 return emitOpError() <<
"unsupported alloc op";
412 auto linalgOp = dyn_cast<linalg::LinalgOp>(operand.
getOwner());
419 Value blockArgument = linalgOp.getMatchingBlockArgument(&operand);
427 if (!isa<TensorType, FloatType, IntegerType>(value.
getType()))
429 return llvm::any_of(value.
getUses(),
439 auto type = dyn_cast<RankedTensorType>(
tensor.getType());
441 return emitSilenceableError() <<
"non-tensor type: " <<
tensor;
455 for (
auto [pos, dim] : llvm::enumerate(type.getShape())) {
456 if (!ShapedType::isDynamic(dim))
461 tensor::DimOp::create(rewriter,
tensor.getLoc(),
tensor, cst);
462 preservedOps.insert(dimOp);
463 dynamicDims.push_back(dimOp);
465 auto allocation = bufferization::AllocTensorOp::create(
466 rewriter,
tensor.getLoc(), type, dynamicDims);
468 if (getMemorySpaceAttr())
469 allocation.setMemorySpaceAttr(getMemorySpaceAttr());
470 Value allocated = allocation;
474 if (needsMaterialization) {
475 auto copy = bufferization::MaterializeInDestinationOp::create(
477 preservedOps.insert(
copy);
478 promoted.push_back(
copy.getResult());
480 promoted.push_back(allocated);
484 results.
setValues(cast<OpResult>(getPromoted()), promoted);
488void transform::PromoteTensorOp::getEffects(
504 FailureOr<linalg::LinalgOp> res =
506 if (succeeded(res)) {
510 return emitDefaultSilenceableFailure(
target);
524 auto decomposableOp = dyn_cast<AggregatedOpInterface>(
target);
525 if (!decomposableOp) {
527 "payload is not a decomposable op");
528 return emitDefaultSilenceableFailure(
target);
531 FailureOr<SmallVector<Value>> maybeNewResults =
532 decomposableOp.decomposeOperation(rewriter);
533 if (
failed(maybeNewResults))
534 return emitDefaultSilenceableFailure(
target);
536 rewriter.
replaceOp(decomposableOp, *maybeNewResults);
537 for (
Value val : *maybeNewResults) {
538 Operation *definition = val.getDefiningOp();
549void transform::EliminateLinalgOpAnchoredEmptyTensorsOp::getEffects(
556transform::EliminateLinalgOpAnchoredEmptyTensorsOp::apply(
560 options.allowReturnAllocsFromLoops =
true;
566 <<
"failed to analyze op";
568 rewriter,
target, state)))
570 <<
"failed to eliminate LinalgOp anchored tensor.empty ops";
583 bool applyCleanup,
bool useForall) {
585 builder,
result, loopTypes,
591 applyCleanup, useForall);
597 bool applyCleanup,
bool useForall) {
605 applyCleanup, useForall);
612 bool applyCleanup,
bool useForall) {
616 build(builder,
result, loopTypes,
target, mixedTileSizes,
617 mixedTileInterchange, applyCleanup, useForall);
624 bool applyCleanup,
bool useForall) {
631 staticTileInterchange);
636 auto staticTileInterchangeAttr =
638 unsigned numExpectedLoops =
639 useForall ? 1 : staticTileSizes.size() - llvm::count(staticTileSizes, 0);
641 resultTypes.reserve(numExpectedLoops);
642 assert((loopTypes.size() == 1 || loopTypes.size() == numExpectedLoops) &&
643 "expected one loop type or as many as loops");
644 if (loopTypes.size() == 1)
645 resultTypes.append(numExpectedLoops, loopTypes[0]);
647 llvm::append_range(resultTypes, loopTypes);
652 dynamicTileInterchange,
655 staticTileInterchangeAttr,
663template <
typename Range>
668 function_ref<FailureOr<scf::SCFTileAndFuseResult>(TilingInterface)>
672 size_t numTargets = llvm::range_size(payloadOps);
675 auto tilingInterfaceOp = dyn_cast<TilingInterface>(
target);
676 if (!tilingInterfaceOp)
677 return transformOp->
emitError(
"only TilingInterface ops are supported");
680 FailureOr<scf::SCFTileAndFuseResult> tiledResults =
681 applyFn(tilingInterfaceOp);
682 if (failed(tiledResults))
687 llvm::append_range(opsToReplace, tiledResults->fusedProducers);
688 for (
Operation *toReplace : opsToReplace) {
689 for (
OpResult res : toReplace->getResults())
690 if (
auto replacement = tiledResults->replacements.lookup(res))
692 if (toReplace->use_empty()) {
698 tiledLinalgOps.push_back(tiledResults->tiledAndFusedOps.front());
699 assert(tiledResults->loops.size() == numLoops &&
700 "Mismatched number of loops, tile and fuse transform should have "
702 for (
unsigned int i = 0; i < numLoops; ++i)
703 loopOps[i].
push_back(tiledResults->loops[i]);
706 transformResults.
set(transformOp->
getOpResult(0), tiledLinalgOps);
715 for (
unsigned int idx = 0; idx < numTargets; ++idx)
716 for (
unsigned int i = 0; i < numLoops; ++i)
717 flattenedLoopOps.push_back(loopOps[i][idx]);
718 transformResults.
set(transformOp->
getOpResult(1), flattenedLoopOps);
720 for (
unsigned int i = 0; i < numLoops; ++i)
721 transformResults.
set(transformOp->
getOpResult(i + 1), loopOps[i]);
731 auto transformOp = cast<TransformOpInterface>(getOperation());
737 state, transformOp, mixedTileSizes, getPackedTileSizes())
739 state, transformOp, mixedTileSizes, getMixedTileSizes());
744 state, transformOp, getMixedTileInterchange(), tileInterchange);
748 scf::SCFTilingOptions tilingOptions;
749 tilingOptions.interchangeVector = std::move(tileInterchange);
750 bool useForall = getUseForall();
751 tilingOptions.setLoopType(useForall
752 ? scf::SCFTilingOptions::LoopType::ForallOp
753 : scf::SCFTilingOptions::LoopType::ForOp);
754 tilingOptions.setTileSizes(mixedTileSizes);
755 scf::SCFTileAndFuseOptions tileAndFuseOptions;
756 tileAndFuseOptions.tilingOptions = std::move(tilingOptions);
759 tileAndFuseOptions.tilingOptions.setInnerTileAlignments(
762 if (getApplyCleanup()) {
765 tensor::ExtractSliceOp::getCanonicalizationPatterns(patterns, context);
768 tileAndFuseOptions.cleanupPatterns = std::move(patterns);
775 numLoops = llvm::count_if(mixedTileSizes, [](
OpFoldResult ofr) {
776 auto attr = dyn_cast<Attribute>(ofr);
779 return cast<IntegerAttr>(attr).getInt() != 0;
783 rewriter, getOperation(), state.
getPayloadOps(getTarget()), numLoops,
784 transformResults, getPackedTileSizes() !=
nullptr,
785 [&](TilingInterface tilingInterfaceOp)
786 -> FailureOr<scf::SCFTileAndFuseResult> {
794LogicalResult transform::FuseOp::verify() {
795 bool hasPackedTiles = getPackedTileSizes() !=
nullptr;
796 if (!getMixedTileSizes().empty() && hasPackedTiles)
798 "tile_sizes and packed_tile_sizes are mutually exclusive");
800 auto iterspace_rank = getStaticTileSizes().size();
802 if (permutation.size() > iterspace_rank)
804 <<
"interchange length exceeds iteration space dimensions ("
805 << iterspace_rank <<
"), found " << getTileInterchange();
807 for (
int64_t v : permutation) {
808 if (!ShapedType::isDynamic(v)) {
809 if (v < 0 || v >=
static_cast<int64_t>(iterspace_rank))
810 return emitOpError() <<
"expects interchange values to be in range [0, "
811 << iterspace_rank <<
"), found: " << v;
813 return emitOpError() <<
"found duplicate interchange value: " << v;
819 size_t numExpectedLoops = getUseForall() || hasPackedTiles
821 : sizes.size() - llvm::count(sizes, 0);
822 if (numExpectedLoops != getNumResults() - 1)
823 return emitOpError() <<
"expects " << numExpectedLoops <<
" loop results";
833 return getMixedValues(getStaticTileInterchange(), getTileInterchange(),
837void transform::FuseOp::getEffects(
851void transform::FuseIntoContainingOp::build(
OpBuilder &builder,
854 Value containingOp) {
855 result.addOperands({producerOp, containingOp});
856 auto resultType = transform::AnyOpType::get(builder.
getContext());
857 result.addTypes({resultType, resultType});
873 (domInfo.
dominates(containingOp, user))) {
874 dominatedUsers.insert(user);
877 if (dominatedUsers.empty())
881 auto forallOp = cast<scf::ForallOp>(containingOp);
887 auto genericOp = dyn_cast<linalg::GenericOp>(producerOp);
892 newOuts.push_back(outputs[resultNumber]);
895 auto newforallOp = scf::ForallOp::create(
896 rewriter, loc, forallOp.getMixedLowerBound(),
897 forallOp.getMixedUpperBound(), forallOp.getMixedStep(), newOuts,
898 forallOp.getMapping());
900 newforallOp.getRegion().takeBody(forallOp.getRegion());
905 newforallOp.getBody()->addArgument(newOuts.back().getType(),
906 newOuts.back().getLoc());
907 auto bbArgs = newforallOp.getBody()->getArguments();
910 Operation *op = use.getOwner();
911 return newforallOp->isProperAncestor(op);
915 scf::InParallelOp terminatorOp = newforallOp.getTerminator();
917 terminatorOp.getYieldingOps(), [](
Operation &op) { return &op; });
918 Operation *firstYieldOp = yieldingOps.front();
921 Value dst = newforallOp.getRegionIterArgs().back();
923 tensor::ParallelInsertSliceOp::create(rewriter, firstYieldOp->
getLoc(), src,
924 dst, offsets, sizes, strides);
926 for (
auto result : llvm::enumerate(forallOp.getResults())) {
928 newforallOp->getResult(
result.index()));
931 newforallOp->getResults().back(),
933 Operation *user = use.getOwner();
934 return dominatedUsers.contains(user);
948 destWorklist.push_back(dst);
950 while (!destWorklist.empty()) {
951 Value currentDst = destWorklist.pop_back_val();
955 if (src == currentDst)
960 auto bbArg = dyn_cast<BlockArgument>(currentDst);
964 Block *parentBlock = bbArg.getOwner();
965 assert(parentBlock &&
"unlinked block argument");
968 assert(parentOp &&
"expected block argument with parent operation");
971 auto parentLoop = dyn_cast<LoopLikeOpInterface>(parentOp);
975 for (
auto innerIterArg : parentLoop.getRegionIterArgs()) {
977 OpOperand *operand = parentLoop.getTiedLoopInit(innerIterArg);
978 Value loopBlockArgument =
980 destWorklist.push_back(loopBlockArgument);
993static std::tuple<SmallVector<Operation *>,
Operation *>
997 LDBG() <<
"Try to fuse a direct extract use";
998 auto tileableProducer = dyn_cast<TilingInterface>(producerOp);
999 if (!tileableProducer) {
1001 <<
"producer is not a TileableInterface: " << *producerOp;
1008 auto it = llvm::find_if(tileableProducer->getUsers(), [&](
Operation *user) {
1009 auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user);
1010 return sliceOp && containingOp->isProperAncestor(sliceOp);
1014 if (it == tileableProducer->getUsers().end()) {
1015 diag.attachNote(tileableProducer->getLoc())
1016 <<
"could not find fusion opportunity for: " << *tileableProducer;
1019 auto sliceOpToTile = cast<tensor::ExtractSliceOp>(*it);
1032 if (LoopLikeOpInterface containerLoop =
1033 dyn_cast<LoopLikeOpInterface>(sliceOpToTile->getParentOp())) {
1039 auto dpsInterface = dyn_cast<DestinationStyleOpInterface>(
clone);
1043 for (
OpOperand &initOperandPtr : dpsInterface.getDpsInitsMutable()) {
1044 Value producerOperand =
1045 clone->getOperand(initOperandPtr.getOperandNumber());
1047 containerLoop.getRegionIterArgs()) {
1048 OpOperand *bbArg = containerLoop.getTiedLoopInit(containerIterArg);
1049 Value consumerOperand =
1053 initOperandPtr.set(containerIterArg);
1059 tileableProducer = dyn_cast<TilingInterface>(
clone);
1064 cast<OpResult>(sliceOpToTile.getSource()).getResultNumber();
1065 LDBG() <<
"resultNumber: " << resultNumber;
1070 FailureOr<TilingResult> tileAndFuseResult =
1071 tileableProducer.generateResultTileValue(rewriter, resultNumber, offsets,
1072 sizes, innerTileAlignments);
1074 if (failed(tileAndFuseResult)) {
1075 diag.attachNote(tileableProducer->getLoc())
1076 <<
"failed to tile producer op: " << *tileableProducer;
1081 for (
auto *tiledOp : tileAndFuseResult->tiledOps) {
1082 LDBG() <<
"tiledProducer: " << *tiledOp;
1087 auto maybeRankReduced = tensor::ExtractSliceOp::rankReduceIfNeeded(
1088 rewriter, sliceOpToTile->getLoc(), tileAndFuseResult->tiledValues[0],
1089 cast<RankedTensorType>(sliceOpToTile->getResult(0).getType()).getShape());
1090 if (failed(maybeRankReduced)) {
1092 <<
"shape types don't match (missing canonicalization?):\nTiledOp: "
1093 << tileAndFuseResult->tiledValues[0]
1094 <<
"\nSliceOp: " << sliceOpToTile.getOperation() <<
'\n';
1097 rewriter.
replaceOp(sliceOpToTile, *maybeRankReduced);
1101 rewriter,
diag, producerOp, containingOp, *tileAndFuseResult,
1102 resultNumber, offsets, sizes);
1105 if (isa<LoopLikeOpInterface>(containingOp))
1106 rewriter.
eraseOp(tileableProducer);
1108 return std::make_tuple(tileAndFuseResult->tiledOps, newContainingOp);
1121 LDBG() <<
"Try to fuse an extract use through block argument";
1123 auto tileableProducer = dyn_cast<TilingInterface>(producerOp);
1124 if (!tileableProducer) {
1126 <<
"producer is not a TileableInterface: " << *producerOp;
1131 scf::ForallOp forallOp;
1132 auto itProducerUses =
1133 llvm::find_if(tileableProducer->getUses(), [&](
OpOperand &use) {
1134 forallOp = dyn_cast<scf::ForallOp>(use.getOwner());
1138 if (!forallOp || forallOp != containingOp) {
1139 diag.attachNote(tileableProducer->getLoc())
1140 <<
"could not find a use by the containing op: " << *tileableProducer;
1155 auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user);
1156 return sliceOp && containingOp->isProperAncestor(sliceOp);
1160 if (itBBArgUsers == bbArg.
getUsers().end()) {
1162 <<
"could not find fusion opportunity for bbArg: " << bbArg;
1165 auto sliceOpToTile = cast<tensor::ExtractSliceOp>(*itBBArgUsers);
1173 int64_t resultNumber = cast<OpResult>(pUse->
get()).getResultNumber();
1174 LDBG() <<
"resultNumber: " << resultNumber;
1179 rewriter, tileableProducer->getLoc(), tileableProducer,
1180 destinationTensors))) {
1181 diag.attachNote(tileableProducer->getLoc())
1182 <<
"failed to get destination tensors for: " << *tileableProducer;
1187 bvm.
map(destinationTensors[resultNumber], bbArg);
1188 auto tileableProducerClone =
1189 cast<TilingInterface>(rewriter.
clone(*tileableProducer, bvm));
1190 llvm::scope_exit scopeGuard(
1191 [&]() { rewriter.
eraseOp(tileableProducerClone); });
1194 FailureOr<TilingResult> tileAndFuseResult =
1195 tileableProducerClone.generateResultTileValue(
1196 rewriter, resultNumber, sliceOpToTile.getMixedOffsets(),
1197 sliceOpToTile.getMixedSizes(), innerTileAlignments);
1198 if (failed(tileAndFuseResult)) {
1199 diag.attachNote(tileableProducer->getLoc())
1200 <<
"failed to tile producer op: " << *tileableProducer;
1205 auto maybeRankReduced = tensor::ExtractSliceOp::rankReduceIfNeeded(
1206 rewriter, sliceOpToTile->getLoc(), tileAndFuseResult->tiledValues[0],
1207 cast<RankedTensorType>(sliceOpToTile->getResult(0).getType()).getShape());
1208 assert(succeeded(maybeRankReduced) &&
"unexpected shape");
1209 rewriter.
replaceOp(sliceOpToTile, *maybeRankReduced);
1214 destinationTensors.front());
1217 return tileAndFuseResult->tiledOps;
1223 LDBG() <<
"Try to fuse an use by cloning";
1230 uses.push_back(&use);
1235 if (containingOp == use.getOwner()) {
1237 <<
"producer op use by containing op cannot be fused by cloning";
1245 diag.attachNote(producerOp->
getLoc()) <<
"no fusion opportunity by cloning";
1254 assert(!isa<tensor::ParallelInsertSliceOp>(use->
getOwner()) &&
1255 "Parallel insert slice is not a valid clone destination");
1256 unsigned resultNumber = cast<OpResult>(use->
get()).getResultNumber();
1257 LDBG() <<
"resultNumber: " << resultNumber;
1261 fusedOp = rewriter.
clone(*producerOp);
1263 use->
getOwner(), [&] { use->set(fusedOp->getOpResult(resultNumber)); });
1268bool transform::FuseIntoContainingOp::allowsRepeatedHandleOperands() {
1273LogicalResult transform::FuseIntoContainingOp::verify() {
1283 auto containingOps = state.
getPayloadOps(getContainingOp());
1284 if (!llvm::hasSingleElement(containingOps)) {
1286 <<
"requires exactly one containing_op handle (got "
1287 << llvm::range_size(containingOps) <<
")";
1289 Operation *containingOp = *containingOps.begin();
1298 if (std::empty(producerOps)) {
1300 results.
set(cast<OpResult>(getNewContainingOp()), {containingOp});
1307 auto getNextProducer = [&]() -> FailureOr<Operation *> {
1308 for (
const auto &it :
enumerate(remainingProducers)) {
1311 int64_t numUsesInContainingOp =
1313 return containingOp->isAncestor(op);
1318 if (numUsesInContainingOp > 0) {
1319 if (numUsesInContainingOp == 1)
1320 remainingProducers.erase(remainingProducers.begin() + it.index());
1327 while (!remainingProducers.empty()) {
1328 auto nextProducer = getNextProducer();
1329 if (
failed(nextProducer)) {
1331 <<
"could not find next producer to fuse into container";
1332 diag.attachNote(containingOp->
getLoc()) <<
"containing op";
1340 diag <<
"could not fuse " << *producerOp <<
" into " << *containingOp;
1348 rewriter,
diag, producerOp, containingOp, innerTileAlignments);
1349 if (!tiledOps.empty()) {
1350 LDBG() <<
"\nFused a direct extract use\n" << *containingOp;
1351 fusedOps.append(tiledOps);
1352 if (newContainingOp) {
1360 LogicalResult replacementStatus =
1363 (
void)replacementStatus;
1364 assert(succeeded(replacementStatus) &&
1365 "unable to update transform state mapping");
1366 rewriter.
eraseOp(containingOp);
1367 containingOp = newContainingOp;
1374 rewriter,
diag, producerOp, containingOp, innerTileAlignments);
1375 if (!tiledContainingOpOperand.empty()) {
1376 LDBG() <<
"\nFused an extract use through block argument\n"
1378 fusedOps.append(tiledContainingOpOperand);
1385 LDBG() <<
"\nFused an use by cloning\n" << *containingOp;
1386 fusedOps.push_back(cloned);
1392 results.
set(cast<OpResult>(getFusedOp()), fusedOps);
1393 results.
set(cast<OpResult>(getNewContainingOp()), {containingOp});
1397void transform::FuseIntoContainingOp::getEffects(
1415 if (isa<GenericOp>(
target)) {
1421 if (succeeded(generic)) {
1422 results.
push_back(generic->getOperation());
1425 return emitDefaultSilenceableFailure(
target);
1438 if (!isa<GenericOp>(
target)) {
1443 FailureOr<LinalgOp> named =
1445 if (succeeded(named)) {
1446 results.
push_back(named->getOperation());
1449 return emitDefaultSilenceableFailure(
target);
1463 if (interchangeVector.empty()) {
1468 unsigned numLoops = cast<LinalgOp>(
target.getOperation()).getNumLoops();
1469 if (interchangeVector.size() != numLoops) {
1470 return emitSilenceableError()
1471 << getIteratorInterchangeAttrName() <<
" has length ("
1472 << interchangeVector.size()
1473 <<
") different from the number of loops in the target operation ("
1484LogicalResult transform::InterchangeOp::verify() {
1486 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, permutation.size()));
1487 if (!std::is_permutation(sequence.begin(), sequence.end(),
1488 permutation.begin(), permutation.end())) {
1489 return emitOpError()
1490 <<
"expects iterator_interchange to be a permutation, found "
1491 << getIteratorInterchange();
1506 if (!isa<linalg::CopyOp>(targetOp)) {
1508 emitSilenceableError() <<
"only linalg.copy target ops are supported";
1509 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1513 auto copyOp = dyn_cast<linalg::CopyOp>(targetOp);
1514 if (!copyOp.hasPureBufferSemantics()) {
1516 emitSilenceableError()
1517 <<
"cannot transform a linalg.copy on tensors into a memref.copy";
1518 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1524 assert(inputs.size() == 1 &&
"expected linalg copy op with one input");
1525 assert(outputs.size() == 1 &&
"expected memref copy op with one output");
1526 Value input = inputs.front();
1527 Value output = outputs.front();
1532 if (!isa<ShapedType>(input.
getType())) {
1534 emitSilenceableError()
1535 <<
"cannot transform a linalg.copy which input has no shape";
1536 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1541 assert(isa<ShapedType>(output.
getType()));
1543 if (cast<ShapedType>(input.
getType()).getElementType() !=
1544 cast<ShapedType>(output.
getType()).getElementType()) {
1546 emitSilenceableError()
1547 <<
"cannot transform a linalg.copy with different source and "
1548 "destination element types ";
1549 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1569 if (!
target.hasPureTensorSemantics()) {
1571 <<
"lower_pack only supports tensor semantics. The target has "
1575 bool lowerPadLikeWithInsertSlice = getLowerPadLikeWithInsertSlice();
1576 FailureOr<LowerPackResult> res =
1580 <<
"cannot lower to pad + expand + transpose";
1583 transformResults.
push_back(res->expandShapeOp);
1584 transformResults.
push_back(res->transposeOp);
1596 if (!
target.hasPureTensorSemantics()) {
1598 emitSilenceableError() <<
"lower_unpack only supports tensor "
1599 "semantics. The target has memref operands";
1600 diag.attachNote(
target->getLoc()) <<
"target payload op";
1604 bool lowerUnpadLikeWithExtractSlice = getLowerUnpadLikeWithExtractSlice();
1605 FailureOr<LowerUnPackOpResult> res =
1609 emitSilenceableError()
1610 <<
"cannot lower to transpose + collapse + extract";
1611 diag.attachNote(
target->getLoc()) <<
"target payload op";
1614 transformResults.
push_back(res->emptyOp);
1615 transformResults.
push_back(res->transposeOp);
1616 transformResults.
push_back(res->collapseShapeOp);
1617 transformResults.
push_back(res->extractSliceOp);
1618 transformResults.
push_back(res->copyOp);
1629 result.addAttribute(MatchOp::getOpsAttrName(
result.name),
1638 result.addAttribute(MatchOp::getOpsAttrName(
result.name),
1640 result.addTypes(resultTypes);
1648 if (getOps().has_value())
1649 strs.insert_range(getOps()->getAsValueRange<StringAttr>());
1652 if (!llvm::hasSingleElement(payloadOps)) {
1657 bool incorrectNumOperandTypes =
false;
1664 if (getInterface().has_value()) {
1665 auto iface = getInterface().value();
1666 if (iface == transform::MatchInterfaceEnum::LinalgOp &&
1669 if (iface == transform::MatchInterfaceEnum::TilingInterface &&
1670 !isa<TilingInterface>(op))
1672 if (iface == transform::MatchInterfaceEnum::LoopLikeInterface &&
1673 !isa<LoopLikeOpInterface>(op))
1678 if (getOpAttrs().has_value()) {
1679 DictionaryAttr opAttrs = getOpAttrs().value();
1681 if (attr.getName() == getInterfaceAttrName() ||
1682 attr.getName() == getOpsAttrName())
1684 std::optional<Attribute> inherent = op->
getInherentAttr(attr.getName());
1690 if (actual != attr.getValue())
1695 if (getFilterResultType().has_value()) {
1696 Type t = getFilterResultType().value();
1701 if (getFilterOperandTypes().has_value()) {
1702 mlir::ArrayAttr types = getFilterOperandTypes().value();
1705 if (types.size() == 1) {
1708 dyn_cast<mlir::TypeAttr>(getFilterOperandTypes().value()[0]);
1709 Type t = cast<::mlir::Type>(typeattr.getValue());
1711 [&](
Type operandType) { return operandType == t; }))
1716 if (types.size() != operandTypes.size()) {
1717 incorrectNumOperandTypes =
true;
1721 for (
auto [attr, operandType] :
1722 llvm::zip_equal(getFilterOperandTypes().value(), operandTypes)) {
1723 auto typeattr = cast<mlir::TypeAttr>(attr);
1724 Type type = cast<::mlir::Type>(typeattr.getValue());
1726 if (type != operandType)
1737 (*payloadOps.begin())->walk(matchFun);
1738 if (incorrectNumOperandTypes)
1740 "type, then it must contain as much types as "
1741 "the number of operands in the target ops");
1742 results.
set(cast<OpResult>(getResult()), res);
1757 Type &targetType,
Type &lowSizeType,
1759 Type &splitPointType) {
1760 FunctionType funcType;
1762 if (failed(parser.
parseType<FunctionType>(funcType)))
1765 if (funcType.getNumInputs() != 1 || funcType.getNumResults() != 1) {
1766 parser.
emitError(typeLoc) <<
"expects a trailing functional type with one "
1767 "argument and one result";
1769 targetType = funcType.getInput(0);
1770 lowSizeType = highSizeType = splitPointType = funcType.getResult(0);
1778 if (isa<TransformParamTypeInterface>(getLowSize().
getType())) {
1779 if (
target.hasDynamicShape()) {
1780 auto diag = emitSilenceableError()
1781 <<
"cannot compute parametric tile sizes for dynamically "
1782 "shaped payload op";
1783 diag.attachNote(
target->getLoc()) <<
"payload op";
1788 target, getDimension(), getTargetSize(), getDivisor());
1790 return emitSilenceableError()
1791 <<
"failed to compute multi-size tiling sizes";
1795 results.
assign(llvm::map_range(
1797 spec->lowTileSize * spec->lowTripCount}),
1798 [&builder,
this](
int64_t value) {
1810 builder,
target, getDimension(), targetSize, divisor);
1812 return emitSilenceableError() <<
"could not generate tile size computation";
1819 {spec->lowTileSize, spec->lowTripCount});
1820 Operation *lowTileSize = spec->lowTileSize.getDefiningOp();
1821 Operation *highTileSize = spec->highTileSize.getDefiningOp();
1822 assert(lowTileSize && highTileSize && splitPoint &&
1823 "tile sizes are not produced by operations");
1831void transform::MultiTileSizesOp::getEffects(
1835 if (isa<TransformParamTypeInterface>(getLowSize().
getType()))
1841LogicalResult transform::MultiTileSizesOp::verify() {
1844 return emitOpError() <<
"expects all results type to be the same";
1863 Type linalgOpHType = transform::OperationType::get(
1864 builder.
getContext(), GenericOp::getOperationName());
1883 if (std::empty(targetOps)) {
1884 transformResults.
set(cast<OpResult>(getPackedOp()),
1889 auto linalgOp = dyn_cast<LinalgOp>(*targetOps.begin());
1890 if (!llvm::hasSingleElement(targetOps) || !linalgOp) {
1891 return emitSilenceableError()
1892 <<
"requires target to map to exactly 1 LinalgOp (got "
1893 << llvm::range_size(targetOps) <<
")";
1896 if (!linalgOp.hasPureTensorSemantics()) {
1897 return emitSilenceableError()
1898 <<
"structured.pack only supports tensor semantics. The target has "
1902 if (getMixedPackedSizes().size() != linalgOp.getNumLoops()) {
1903 return emitSilenceableError()
1904 <<
"requires number of packed sizes match the number of loops ("
1905 << getMixedPackedSizes().size() <<
" vs " << linalgOp.getNumLoops()
1912 state, *
this, packedSizes, getMixedPackedSizes());
1915 FailureOr<PackResult> maybeResult =
pack(rewriter, linalgOp, packedSizes);
1919 transformResults.
set(cast<OpResult>(getPackedOp()),
1920 {maybeResult->packedLinalgOp.getOperation()});
1924void transform::PackOp::getEffects(
1936LogicalResult transform::PackGreedilyOp::verify() {
1938 return emitOpError() << getMatmulInnerDimsOrderAttrName()
1939 <<
" is not a valid permutation";
1942 if (!getMatmulPaddedSizesNextMultipleOf().empty()) {
1943 for (
auto [s, nmo] :
1944 llvm::zip_equal(getMixedMatmulPackedSizes(),
1945 getMatmulPaddedSizesNextMultipleOf())) {
1948 (!maybeStaticPackedSize.has_value() || *maybeStaticPackedSize != 0)) {
1949 return emitOpError() <<
"at most one of the packed_size and the "
1950 "padded_sizes_next_multiple_of can be nonzero "
1951 "for the matmul strategy";
1963 for (
auto linalgOp :
1964 llvm::make_isa_range<LinalgOp>(state.
getPayloadOps(getTarget()))) {
1973 getMixedMatmulPackedSizes(),
1975 getMatmulPaddedSizesNextMultipleOf(),
1976 getMatmulInnerDimsOrder());
1977 if (succeeded(packResult)) {
1978 results.push_back(packResult->packedLinalgOp);
1981 results.push_back(linalgOp);
1983 transformResults.
set(cast<OpResult>(getPackedOp()), results);
1989 return getMixedValues(getStaticMatmulPackedSizes(), getMatmulPackedSizes(),
1993void transform::PackGreedilyOp::getEffects(
2005LogicalResult transform::PackTransposeOp::verify() {
2007 return emitOpError() << getInnerPermAttrName()
2008 <<
" is not a valid permutation";
2011 return emitOpError() << getOuterPermAttrName()
2012 <<
" is not a valid permutation";
2014 if (getInnerPerm().empty() && getOuterPerm().empty()) {
2015 return emitOpError() <<
" at least one of " << getInnerPermAttrName()
2016 <<
" or " << getOuterPermAttrName()
2017 <<
" must be specified";
2023enum class OuterOrInnerPerm { Outer = 0, Inner = 1 };
2033template <
typename RelayoutOpTy>
2034static bool isValidPackingPermutation(
2036 OuterOrInnerPerm outerOrInnerPerm = OuterOrInnerPerm::Outer) {
2038 llvm::is_one_of<RelayoutOpTy, linalg::PackOp, linalg::UnPackOp>::value,
2039 "applies to only pack or unpack operations");
2040 if (!op || permutation.empty())
2042 size_t innerRank = op.getInnerDimsPos().size();
2043 if (outerOrInnerPerm == OuterOrInnerPerm::Inner)
2047 if (std::is_same<RelayoutOpTy, linalg::PackOp>::value) {
2048 return permutation.size() == op.getSourceRank() &&
2051 return permutation.size() == op.getDestRank() &&
2059 auto packOrUnpackOps = state.
getPayloadOps(getTargetPackOrUnPackOp());
2062 if (std::empty(packOrUnpackOps)) {
2063 transformResults.
set(cast<OpResult>(getPackedOp()), {});
2064 transformResults.
set(cast<OpResult>(getPackOp()), {});
2065 transformResults.
set(cast<OpResult>(getUnPackOp()), {});
2071 if (!llvm::hasSingleElement(packOrUnpackOps) ||
2072 !llvm::hasSingleElement(linalgOps)) {
2073 return emitSilenceableError()
2074 <<
"requires target to map to exactly 1 "
2075 "packing op and 1 packed op ("
2076 <<
"got " << llvm::range_size(packOrUnpackOps) <<
" and "
2077 << llvm::range_size(linalgOps) <<
")";
2081 auto packOp = dyn_cast<linalg::PackOp>(*packOrUnpackOps.begin());
2082 auto unPackOp = dyn_cast<linalg::UnPackOp>(*packOrUnpackOps.begin());
2083 if ((!packOp && !unPackOp)) {
2084 return emitSilenceableError() <<
"requires target to map to a "
2085 "linalg.pack or linalg.unpack";
2087 LinalgOp linalgOpTarget = dyn_cast<LinalgOp>(*linalgOps.begin());
2088 if (!linalgOpTarget)
2089 return emitSilenceableError() <<
"requires a LinalgOp target";
2093 if (packOp && packOp.getResult().hasOneUse())
2094 linalgOp = dyn_cast<LinalgOp>(*(packOp.getResult().getUsers().begin()));
2096 linalgOp = unPackOp.getSource().getDefiningOp<LinalgOp>();
2097 if (linalgOp != linalgOpTarget) {
2099 packOp ? StringLiteral{
"not a single use by the LinalgOp target"}
2100 : StringLiteral{
"not produced by the LinalgOp target"};
2101 return emitSilenceableError() << errorMsg;
2107 assert(!packOp &&
"packOp must be null on entry when unPackOp is not null");
2108 OpOperand *packUse = linalgOp.getDpsInitOperand(
2109 cast<OpResult>(unPackOp.getSource()).getResultNumber());
2111 if (!packOp || !packOp.getResult().hasOneUse())
2112 return emitSilenceableError() <<
"could not find matching pack op";
2116 for (
auto permType : {OuterOrInnerPerm::Outer, OuterOrInnerPerm::Inner}) {
2118 (permType == OuterOrInnerPerm::Outer) ? getOuterPerm() : getInnerPerm();
2119 auto errorMsg = (permType == OuterOrInnerPerm::Outer)
2120 ? StringLiteral{
"invalid outer_perm"}
2121 : StringLiteral{
"invalid inner_perm"};
2122 if (!isValidPackingPermutation(packOp, perm, permType) ||
2123 !isValidPackingPermutation(unPackOp, perm, permType)) {
2125 unPackOp ? unPackOp.getOperation() : packOp.getOperation();
2126 return emitSilenceableError() << errorMsg <<
": " << *packOrUnpackOp;
2132 assert(packOp && linalgOp &&
"unexpected null op");
2136 rewriter, packOp, linalgOp, unPackOp, getOuterPerm(), getInnerPerm());
2138 assert(succeeded(res) &&
"unexpected packTranspose failure");
2141 transformResults.
set(cast<OpResult>(getPackOp()), {res->transposedPackOp});
2142 transformResults.
set(cast<OpResult>(getPackedOp()),
2143 {res->transposedLinalgOp});
2145 transformResults.
set(cast<OpResult>(getUnPackOp()),
2146 {res->transposedUnPackOp});
2148 transformResults.
set(cast<OpResult>(getUnPackOp()), {});
2163 StringRef copyBackOp,
2164 bool usePrescribedTensorShapes) {
2165 auto resultType = transform::AnyOpType::get(
b.getContext());
2171 b.getI64ArrayAttr(paddingDimensions),
2174 (padToMultipleOf.empty()
2176 :
b.getDenseI64ArrayAttr(padToMultipleOf)),
2177 b.getI64ArrayAttr(nofoldFlags),
2178 b.getArrayAttr(transposePaddings),
2179 b.getStringAttr(copyBackOp),
2181 usePrescribedTensorShapes ?
b.getUnitAttr() :
nullptr);
2189 StringRef copyBackOp,
2190 bool usePrescribedTensorShapes) {
2191 auto resultType = transform::AnyOpType::get(
b.getContext());
2195 staticPadToMultipleOf);
2201 b.getI64ArrayAttr(paddingDimensions),
2202 dynamicPadToMultipleOf,
2203 staticPadToMultipleOf,
2204 b.getI64ArrayAttr(nofoldFlags),
2205 b.getArrayAttr(transposePaddings),
2207 usePrescribedTensorShapes);
2210void PadOp::getEffects(
2218SmallVector<OpFoldResult> PadOp::getMixedPadToMultipleOf() {
2220 return getMixedValues(getStaticPadToMultipleOf(), getPadToMultipleOf(),
b);
2223DiagnosedSilenceableFailure
2224transform::PadOp::apply(transform::TransformRewriter &rewriter,
2225 transform::TransformResults &results,
2226 transform::TransformState &state) {
2227 auto transformOp = cast<TransformOpInterface>(getOperation());
2228 SmallVector<Operation *> paddedOps, padOps, copyBackOps;
2231 auto linalgTarget = dyn_cast<LinalgOp>(
target);
2232 if (!linalgTarget) {
2233 auto diag = emitSilenceableError() <<
"expected LinalgOp target";
2239 SmallVector<bool> nofoldFlags;
2240 for (int64_t packPadding :
2242 nofoldFlags.push_back(
static_cast<bool>(packPadding));
2245 SmallVector<Attribute> paddingValues;
2246 for (
auto const &[untypedAttr, elementOrTensorType] :
2247 llvm::zip(getPaddingValues(), linalgTarget->getOperandTypes())) {
2250 paddingValues.push_back(untypedAttr);
2253 auto attr = dyn_cast<TypedAttr>(untypedAttr);
2255 emitOpError(
"expects padding values to be typed attributes or poison");
2260 if (
auto stringAttr = dyn_cast<StringAttr>(attr)) {
2264 if (!parsedAttr || parsedAttr.getType() != elementType) {
2265 auto diag = this->emitOpError(
"expects a padding that parses to ")
2266 << elementType <<
", got " << untypedAttr;
2267 diag.
attachNote(linalgTarget.getLoc()) <<
"when applied to this op";
2270 paddingValues.push_back(parsedAttr);
2274 if (attr.getType() != elementType) {
2275 auto diag = this->emitOpError(
"expects a padding value of type ")
2276 << elementType <<
", got " << attr;
2277 diag.
attachNote(linalgTarget.getLoc()) <<
"when applied to this op";
2280 paddingValues.push_back(attr);
2284 SmallVector<SmallVector<int64_t>> transposePaddings;
2285 for (Attribute transposeVector : cast<ArrayAttr>(getTransposePaddings()))
2287 cast<ArrayAttr>(transposeVector)));
2294 SmallVector<int64_t> padToMultipleOf;
2296 state, transformOp, getMixedPadToMultipleOf(), padToMultipleOf);
2299 if (padToMultipleOf.empty())
2301 SmallVector<int64_t>(
options.paddingDimensions.size(), 1);
2303 options.padToMultipleOf = std::move(padToMultipleOf);
2304 options.paddingValues = std::move(paddingValues);
2305 options.nofoldFlags = std::move(nofoldFlags);
2306 if (getCopyBackOp() ==
2307 bufferization::MaterializeInDestinationOp::getOperationName()) {
2308 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::
2309 BufferizationMaterializeInDestination;
2310 }
else if (getCopyBackOp() == linalg::CopyOp::getOperationName()) {
2311 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::LinalgCopy;
2312 }
else if (getCopyBackOp() == kCopyOpNone) {
2313 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::None;
2315 llvm_unreachable(
"unsupported copy_back op");
2318 bool irChanged =
false;
2319 if (getUsePrescribedTensorShapes() &&
2320 linalgTarget.hasPureTensorSemantics()) {
2321 OpBuilder::InsertionGuard g(rewriter);
2323 for (OpOperand &operand : linalgTarget->getOpOperands()) {
2324 for (
auto [i, dim] : llvm::enumerate(linalgTarget.getShape(&operand))) {
2325 if (ShapedType::isStatic(dim))
2327 options.setSizeToPadTo(operand.getOperandNumber(), i,
2329 operand.get().getLoc(),
2336 SmallVector<Value> replacements;
2337 SmallVector<tensor::PadOp> newPadOps;
2339 replacements, newPadOps))) {
2345 auto diag = emitSilenceableError() <<
"failed to pad op";
2355 rewriter.
replaceOp(linalgTarget, replacements);
2356 paddedOps.push_back(paddedOp);
2357 padOps.append(newPadOps.begin(), newPadOps.end());
2358 if (
options.copyBackOp != LinalgPaddingOptions::CopyBackOp::None) {
2359 for (Value v : replacements) {
2360 Operation *copyBackOp = v.getDefiningOp();
2361 if (!llvm::is_contained(copyBackOps, copyBackOp))
2362 copyBackOps.push_back(copyBackOp);
2367 results.
set(cast<OpResult>(getPadded()), paddedOps);
2368 results.
set(cast<OpResult>(getPad()), padOps);
2369 results.
set(cast<OpResult>(getCopy()), copyBackOps);
2373LogicalResult transform::PadOp::verify() {
2374 SmallVector<int64_t> nofoldFlags =
2376 if (any_of(nofoldFlags, [](int64_t packPadding) {
2377 return packPadding != 0 && packPadding != 1;
2379 return emitOpError()
2380 <<
"expects nofold_flags to contain booleans (0/1), found "
2381 << getNofoldFlags();
2384 SmallVector<int64_t> paddingDimensions =
2386 if (any_of(paddingDimensions,
2387 [](int64_t paddingDimension) {
return paddingDimension < 0; })) {
2388 return emitOpError() <<
"expects padding_dimensions to contain positive "
2390 << getPaddingDimensions();
2392 if (!getMixedPadToMultipleOf().empty()) {
2393 if (getMixedPadToMultipleOf().size() != paddingDimensions.size()) {
2394 return emitOpError() <<
"expects as many multiples as padding_dimensions";
2397 ArrayAttr transposes = getTransposePaddings();
2398 for (Attribute attr : transposes) {
2400 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2401 if (!std::is_permutation(sequence.begin(), sequence.end(),
2402 transpose.begin(), transpose.end())) {
2403 return emitOpError()
2404 <<
"expects transpose_paddings to be a permutation, found "
2408 if (getCopyBackOp() !=
2409 bufferization::MaterializeInDestinationOp::getOperationName() &&
2410 getCopyBackOp() != linalg::CopyOp::getOperationName() &&
2411 getCopyBackOp() != kCopyOpNone)
2412 return emitOpError() <<
"invalid copy_back_op";
2420void transform::PadTilingInterfaceOp::build(OpBuilder &
b,
2423 ArrayRef<int64_t> paddingSizes,
2424 bool padToMultipleOf) {
2425 auto resultType = transform::AnyOpType::get(
b.getContext());
2434 :
b.getDenseI64ArrayAttr(paddingSizes)),
2436 padToMultipleOf ?
b.getUnitAttr() :
nullptr);
2439void transform::PadTilingInterfaceOp::build(
2441 ArrayRef<OpFoldResult> mixedPaddingSizes,
bool padToMultipleOf) {
2442 auto resultType = transform::AnyOpType::get(
b.getContext());
2443 SmallVector<int64_t> staticPaddingSizes;
2444 SmallVector<Value> dynamicPaddingSizes;
2446 staticPaddingSizes);
2452 dynamicPaddingSizes,
2457void transform::PadTilingInterfaceOp::getEffects(
2458 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2465SmallVector<OpFoldResult>
2466transform::PadTilingInterfaceOp::getMixedPaddingSizes() {
2471DiagnosedSilenceableFailure
2472transform::PadTilingInterfaceOp::apply(transform::TransformRewriter &rewriter,
2473 transform::TransformResults &results,
2474 transform::TransformState &state) {
2475 SmallVector<Operation *> paddedOps, padOps;
2478 auto targetOp = dyn_cast<TilingInterface>(
target);
2480 auto diag = emitSilenceableError() <<
"expected TilingInterface target";
2488 if (!isa<IndexingMapOpInterface>(targetOp.getOperation())) {
2489 auto diag = emitSilenceableError() <<
"only IndexingMapOpInterface ops "
2496 SmallVector<Attribute> paddingValues;
2497 for (
auto const &[untypedAttr, elementOrTensorType] :
2498 llvm::zip(getPaddingValues(), targetOp->getOperandTypes())) {
2499 auto attr = dyn_cast<TypedAttr>(untypedAttr);
2503 paddingValues.push_back(untypedAttr);
2507 emitOpError(
"expects padding values to be typed attributes or poison");
2511 if (
auto stringAttr = dyn_cast<StringAttr>(attr)) {
2515 if (!parsedAttr || parsedAttr.getType() != elementType) {
2516 auto diag = this->emitOpError(
"expects a padding that parses to ")
2517 << elementType <<
", got " << attr;
2518 diag.
attachNote(targetOp.getLoc()) <<
"when applied to this op";
2521 paddingValues.push_back(parsedAttr);
2525 if (attr.getType() != elementType) {
2526 auto diag = this->emitOpError(
"expects a padding value of type ")
2527 << elementType <<
", got " << attr;
2528 diag.
attachNote(targetOp.getLoc()) <<
"when applied to this op";
2531 paddingValues.push_back(attr);
2535 PadTilingInterfaceOptions
options;
2536 options.paddingValues = std::move(paddingValues);
2537 options.setPaddingSizes(getMixedPaddingSizes())
2538 .setPadToMultipleOf(getPadToMultipleOf());
2540 OpBuilder::InsertionGuard g(rewriter);
2543 rewriter, cast<TilingInterface>(targetOp.getOperation()),
2545 if (
failed(maybePadOps)) {
2546 auto diag = emitSilenceableError() <<
"failed to pad op";
2550 const auto &[paddedOperands, paddedOp, slicedResults] = maybePadOps.value();
2553 paddedOps.push_back(paddedOp);
2554 padOps.append(paddedOperands.begin(), paddedOperands.end());
2555 rewriter.
replaceOp(targetOp.getOperation(), slicedResults);
2558 results.
set(cast<OpResult>(getPadded()), paddedOps);
2559 results.
set(cast<OpResult>(getPad()), padOps);
2563LogicalResult transform::PadTilingInterfaceOp::verify() {
return success(); }
2569DiagnosedSilenceableFailure transform::HoistPadBuildPackingLoopNestOp::apply(
2570 transform::TransformRewriter &rewriter,
2571 transform::TransformResults &transformResults,
2572 transform::TransformState &state) {
2575 if (!llvm::hasSingleElement(targetOps) || !llvm::hasSingleElement(loopOps)) {
2577 <<
"requires exactly one target and one loop handle (got "
2578 << llvm::range_size(targetOps) <<
" and "
2579 << llvm::range_size(loopOps) <<
")";
2582 auto padOp = dyn_cast_or_null<tensor::PadOp>(*targetOps.begin());
2583 auto loopOp = dyn_cast_or_null<scf::ForOp>(*loopOps.begin());
2584 if (!padOp || !loopOp)
2587 FailureOr<linalg::detail::PackingResult>
result =
2593 if (
result->clonedLoopIvs.empty()) {
2594 transformResults.
set(cast<OpResult>(getPackingLoop()),
2595 {
result->hoistedPadOp.getOperation()});
2598 auto outerPackedLoop =
2600 transformResults.
set(cast<OpResult>(getPackingLoop()),
2601 {outerPackedLoop.getOperation()});
2605LogicalResult transform::HoistPadBuildPackingLoopNestOp::verify() {
2606 ArrayRef<int64_t> transpose = getTranspose();
2607 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2608 if (!std::is_permutation(sequence.begin(), sequence.end(), transpose.begin(),
2610 return emitOpError() <<
"expects transpose to be a permutation, found "
2616void transform::HoistPadBuildPackingLoopNestOp::getEffects(
2617 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2624DiagnosedSilenceableFailure
2625transform::HoistPadOp::applyToOne(transform::TransformRewriter &rewriter,
2627 transform::ApplyToEachResultList &results,
2628 transform::TransformState &state) {
2629 tensor::PadOp hoistedPadOp;
2630 SmallVector<TransposeOp> transposeOps;
2631 FailureOr<Value>
result =
2633 hoistedPadOp, transposeOps);
2644 return emitDefaultSilenceableFailure(
target);
2647LogicalResult transform::HoistPadOp::verify() {
2648 ArrayRef<int64_t> transpose = getTranspose();
2649 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2650 if (!std::is_permutation(sequence.begin(), sequence.end(), transpose.begin(),
2652 return emitOpError() <<
"expects transpose to be a permutation, found "
2662DiagnosedSilenceableFailure
2663transform::PromoteOp::applyToOne(transform::TransformRewriter &rewriter,
2665 transform::ApplyToEachResultList &results,
2666 transform::TransformState &state) {
2667 LinalgPromotionOptions promotionOptions;
2668 if (!getOperandsToPromote().empty())
2671 if (getUseFullTilesByDefault())
2673 getUseFullTilesByDefault());
2674 if (getUseOriginalSubviewSize())
2678 promotionOptions = promotionOptions.
setUseAlloca(getUseAlloca());
2679 if (!getUseFullTileBuffers().empty())
2681 llvm::to_vector(getUseFullTileBuffers().getAsValueRange<BoolAttr>()));
2682 if (getAlignment().has_value())
2683 promotionOptions = promotionOptions.
setAlignment(*getAlignment());
2684 if (getMemorySpace().has_value())
2685 promotionOptions = promotionOptions.
setMemorySpace(*getMemorySpace());
2687 if (getMapping().has_value()) {
2689 auto mapping = *getMapping();
2690 if (mapping.size() > 1)
2691 return emitDefaultDefiniteFailure(
target);
2693 auto addressSpace = cast<mlir::gpu::GPUMemorySpaceMappingAttr>(mapping[0]);
2695 if (addressSpace.getAddressSpace() ==
2696 mlir::gpu::GPUDialect::getWorkgroupAddressSpace()) {
2703 }
else if (addressSpace.getAddressSpace() ==
2704 mlir::gpu::GPUDialect::getPrivateAddressSpace()) {
2712 return emitDefaultDefiniteFailure(
target);
2717 return emitDefaultDefiniteFailure(
target);
2722 return emitDefaultDefiniteFailure(
target);
2731DiagnosedSilenceableFailure
2732transform::ReplaceOp::apply(transform::TransformRewriter &rewriter,
2733 TransformResults &transformResults,
2734 TransformState &state) {
2738 for (Operation *
target : payload) {
2739 if (
target->getNumOperands() > 0)
2741 if (!
target->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
2742 target->getNumRegions() > 0)
2744 <<
"expected target that is isolated from above";
2748 Operation *pattern = &getBodyRegion().front().front();
2749 SmallVector<Operation *> replacements;
2750 for (Operation *
target : payload) {
2751 if (getOperation()->isAncestor(
target))
2758 transformResults.
set(cast<OpResult>(getReplacement()), replacements);
2762void transform::ReplaceOp::getEffects(
2763 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2769LogicalResult transform::ReplaceOp::verify() {
2770 if (!getBodyRegion().hasOneBlock())
2771 return emitOpError() <<
"expected one block";
2772 if (std::distance(getBodyRegion().front().begin(),
2773 getBodyRegion().front().end()) != 1)
2774 return emitOpError() <<
"expected one operation in block";
2775 Operation *
replacement = &getBodyRegion().front().front();
2778 <<
"expected replacement without operands";
2779 if (!
replacement->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
2782 <<
"expect op that is isolated from above";
2790DiagnosedSilenceableFailure
2791transform::ScalarizeOp::applyToOne(transform::TransformRewriter &rewriter,
2793 transform::ApplyToEachResultList &results,
2794 transform::TransformState &state) {
2795 scf::SCFTilingOptions tilingOptions;
2796 tilingOptions.setTileSizeComputationFunction([&](OpBuilder &
b, Operation *) {
2797 SmallVector<OpFoldResult> tileSizes;
2798 Location loc =
target.getLoc();
2799 SmallVector<OpFoldResult> allShapeSizes =
2800 target.createFlatListOfOperandDims(
b, loc);
2801 AffineMap map =
target.getShapesToLoopsMap();
2804 SmallVector<OpFoldResult> shapeSizes =
2809 for (OpFoldResult shapeSize : shapeSizes) {
2811 :
b.getIndexAttr(1));
2816 FailureOr<scf::SCFTilingResult> maybeTilingResult =
tileUsingSCF(
2817 rewriter, cast<TilingInterface>(
target.getOperation()), tilingOptions);
2818 if (
failed(maybeTilingResult))
2819 return emitDefaultDefiniteFailure(
target);
2821 if (
target->getNumResults())
2826 results.
reserve(maybeTilingResult->tiledOps.size());
2827 for (Operation *tiled : maybeTilingResult->tiledOps)
2836DiagnosedSilenceableFailure
2837transform::ConvertToLoopsOp::apply(transform::TransformRewriter &rewriter,
2838 transform::TransformResults &results,
2839 transform::TransformState &state) {
2840 SmallVector<Operation *> loops;
2842 auto tilingOp = dyn_cast<TilingInterface>(*
target);
2844 DiagnosedSilenceableFailure diag =
2845 emitSilenceableError()
2846 <<
"expected the payload to implement TilingInterface";
2851 FailureOr<SmallVector<scf::ForOp>> generatedLoops =
2852 scf::lowerToLoopsUsingSCFForOp(rewriter, tilingOp);
2853 if (
failed(generatedLoops))
2854 return emitDefaultDefiniteFailure(
target);
2855 for (scf::ForOp &loop : *generatedLoops) {
2856 loops.push_back(loop.getOperation());
2860 results.
set(cast<OpResult>(getResult()), loops);
2868DiagnosedSilenceableFailure
2869transform::RewriteInDestinationPassingStyleOp::applyToOne(
2870 transform::TransformRewriter &rewriter, Operation *
target,
2871 transform::ApplyToEachResultList &results,
2872 transform::TransformState &state) {
2874 FailureOr<Operation *> maybeResult =
2876 .Case<DestinationStyleOpInterface>([](
auto op) {
return op; })
2877 .Case<tensor::FromElementsOp, tensor::GenerateOp, tensor::PadOp>(
2878 [&rewriter](
auto op) {
2882 return emitDefaultSilenceableFailure(
target);
2891DiagnosedSilenceableFailure
2892SplitOp::apply(transform::TransformRewriter &rewriter,
2893 TransformResults &results, TransformState &state) {
2895 SmallVector<Operation *> payload =
2898 bool isMultiwaySplit = getMultiway();
2900 if (isMultiwaySplit && !llvm::hasSingleElement(payload)) {
2902 <<
"requires exactly one target when "
2903 "multiway split is enabled (got "
2904 << llvm::range_size(payload) <<
")";
2907 SmallVector<OpFoldResult> chunkSizes;
2909 if (!isMultiwaySplit)
2910 chunkSizes.reserve(payload.size());
2912 if (getDynamicChunkSizes()) {
2914 if (isa<TransformHandleTypeInterface>(getDynamicChunkSizes().
getType())) {
2915 chunkSizes = llvm::map_to_vector(
2916 state.
getPayloadOps(getDynamicChunkSizes()), [&](Operation *op) {
2919 diag = emitSilenceableError()
2920 <<
"expected dynamic split point handle to point to a "
2921 "single-result index-typed op";
2927 chunkSizes = llvm::map_to_vector(
2928 state.
getParams(getDynamicChunkSizes()),
2929 [](Attribute attr) {
return OpFoldResult(attr); });
2936 if (!isMultiwaySplit && chunkSizes.size() != payload.size()) {
2938 <<
"expected the dynamic split point handle to point to as "
2940 << chunkSizes.size() <<
") as the target handle ("
2941 << payload.size() <<
")";
2944 chunkSizes.resize(payload.size(),
2948 auto checkStructuredOpAndDimensions =
2949 [&](LinalgOp linalgOp, Location loc) -> DiagnosedSilenceableFailure {
2951 auto diag = emitSilenceableError() <<
"only applies to structured ops";
2956 if (getDimension() >= linalgOp.getNumLoops()) {
2957 auto diag = emitSilenceableError() <<
"dimension " << getDimension()
2958 <<
" does not exist in target op";
2965 auto checkFailureInSplitting =
2966 [&](
bool hasFailed, Location loc) -> DiagnosedSilenceableFailure {
2975 SmallVector<Operation *> opList;
2976 if (isMultiwaySplit) {
2979 TilingInterface head, tail;
2980 Operation *
target = payload.front();
2982 LinalgOp linalgOp = dyn_cast<LinalgOp>(
target);
2985 DiagnosedSilenceableFailure diag =
2986 checkStructuredOpAndDimensions(linalgOp,
target->getLoc());
2990 for (
auto &&[idx, chunkSize] : llvm::enumerate(chunkSizes)) {
2993 target = tail.getOperation();
2998 linalgOp = cast<LinalgOp>(
target);
2999 Location loc =
target->getLoc();
3003 rewriter, cast<TilingInterface>(linalgOp.getOperation()),
3004 getDimension(), chunkSize);
3007 DiagnosedSilenceableFailure diag =
3008 checkFailureInSplitting(!head && !tail, loc);
3012 opList.push_back(head.getOperation());
3017 opList.push_back(tail.getOperation());
3021 SmallVector<Operation *> first, second;
3022 Operation *noSecondPart =
nullptr;
3023 for (
const auto &pair : llvm::zip(payload, chunkSizes)) {
3024 Operation *
target = std::get<0>(pair);
3025 Location loc =
target->getLoc();
3026 LinalgOp linalgOp = dyn_cast<LinalgOp>(
target);
3027 DiagnosedSilenceableFailure diag =
3028 checkStructuredOpAndDimensions(linalgOp,
target->getLoc());
3034 std::tie(first.emplace_back(), second.emplace_back()) =
linalg::splitOp(
3035 rewriter, cast<TilingInterface>(linalgOp.getOperation()),
3036 getDimension(), std::get<1>(pair));
3039 DiagnosedSilenceableFailure diagSplit =
3040 checkFailureInSplitting(!first.back() && !second.back(), loc);
3045 if (!second.back()) {
3051 if (second.size() != first.size() && !second.empty()) {
3052 auto diag = emitSilenceableError()
3053 <<
"splitting does not produce the second part for a subset "
3056 <<
"expected splitting to produce the second part of all "
3057 "or none of the targets";
3059 <<
"first target with no second part";
3063 opList.append(first);
3064 if (!second.empty())
3065 opList.append(second);
3067 results.
set(cast<OpResult>(getSplitList()), opList);
3071void SplitOp::getEffects(
3072 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3074 if (getDynamicChunkSizes())
3080ParseResult SplitOp::parse(OpAsmParser &parser, OperationState &
result) {
3081 OpAsmParser::UnresolvedOperand
target, dynamicChunkSizes;
3082 IntegerAttr staticChunkSizes;
3086 OptionalParseResult dynamicPointParseResult =
3088 if (!dynamicPointParseResult.
has_value()) {
3089 int64_t staticChunkSizesValue;
3103 if (dynamicPointParseResult.
has_value()) {
3104 Type chunkSizesType;
3117 SplitOp::getStaticChunkSizesAttrName(
result.name).getValue(),
3119 result.addTypes(targetType);
3123void SplitOp::print(OpAsmPrinter &printer) {
3124 printer <<
" " << getTarget() <<
" after ";
3125 int64_t staticChunkSize =
static_cast<int64_t
>(getStaticChunkSizes());
3126 if (staticChunkSize != ShapedType::kDynamic)
3127 printer << staticChunkSize;
3129 printer << getDynamicChunkSizes();
3131 NamedAttrList attrs(getOperation()->getDiscardableAttrDictionary());
3132 attrs.append(getDimensionAttrName(), getDimensionAttr());
3133 if (UnitAttr multiway = getMultiwayAttr())
3134 attrs.append(getMultiwayAttrName(), multiway);
3136 printer <<
" : " << getTarget().getType();
3137 if (staticChunkSize == ShapedType::kDynamic)
3138 printer <<
", " << getDynamicChunkSizes().getType();
3141LogicalResult SplitOp::verify() {
3142 if ((
static_cast<int64_t
>(getStaticChunkSizes()) != ShapedType::kDynamic) ^
3143 (getDynamicChunkSizes() ==
nullptr)) {
3144 return emitOpError() <<
"expects either a dynamic or a static split "
3145 "point to be provided";
3154void transform::SplitReductionOp::build(
3155 OpBuilder &builder, OperationState &
result, Value
target,
3156 int64_t splitFactor, int64_t insertSplitDimension,
bool innerParallel,
3157 bool useScalingAlgorithm,
bool useAlloc) {
3160 result.addAttribute(SplitReductionOp::getSplitFactorAttrName(
result.name),
3163 SplitReductionOp::getInsertSplitDimensionAttrName(
result.name),
3165 if (innerParallel) {
3166 result.addAttribute(SplitReductionOp::getInnerParallelAttrName(
result.name),
3169 if (useScalingAlgorithm) {
3171 SplitReductionOp::getUseScalingAlgorithmAttrName(
result.name),
3175 result.addAttribute(SplitReductionOp::getUseAllocAttrName(
result.name),
3178 auto resultType = transform::AnyOpType::get(ctx);
3179 result.addTypes({resultType, resultType, resultType, resultType});
3182DiagnosedSilenceableFailure transform::SplitReductionOp::applyToOne(
3183 transform::TransformRewriter &rewriter, LinalgOp
target,
3184 transform::ApplyToEachResultList &results,
3185 transform::TransformState &state) {
3187 return linalg::SplitReductionOptions{int64_t(getSplitFactor()),
3188 unsigned(getInsertSplitDimension()),
3189 bool(getInnerParallel())};
3192 FailureOr<SplitReductionResult> splitResult =
3193 (getUseScalingAlgorithm())
3197 return emitDefaultDefiniteFailure(
target);
3199 results.
push_back(splitResult->initOrAlloc);
3201 results.
push_back(splitResult->splitLinalgOp);
3202 results.
push_back(splitResult->resultCombiningLinalgOp);
3210void transform::TileReductionUsingForOp::build(
3211 OpBuilder &builder, OperationState &
result, Value
target,
3212 ArrayRef<int64_t> staticTileSizes) {
3219 auto opTy = transform::AnyOpType::get(ctx);
3225 staticTileSizesAttr);
3228DiagnosedSilenceableFailure transform::TileReductionUsingForOp::applyToOne(
3229 transform::TransformRewriter &rewriter, Operation *
target,
3230 transform::ApplyToEachResultList &results,
3231 transform::TransformState &state) {
3234 auto partialReductionOp = dyn_cast<PartialReductionOpInterface>(
target);
3235 if (!partialReductionOp) {
3238 "Operation should implement PartialReductionOpInterface");
3241 SmallVector<unsigned> reductionDims =
3243 if (reductionDims.empty()) {
3244 for (
auto [idx, iteratorType] :
3245 llvm::enumerate(partialReductionOp.getLoopIteratorTypes())) {
3246 if (iteratorType == utils::IteratorType::reduction)
3247 reductionDims.push_back(idx);
3251 scf::SCFTilingOptions
options;
3252 options.setLoopType(scf::SCFTilingOptions::LoopType::ForOp);
3253 options.setReductionTilingStrategy(
3256 options.setReductionDims(reductionDims);
3257 FailureOr<scf::SCFTilingResult>
result =
3258 scf::tileUsingSCF(rewriter, partialReductionOp,
options);
3262 "failed to tile using partial reduction");
3265 for (Value initValue :
result->initialValues)
3267 for (
auto *parallelTiledOp :
result->tiledOps)
3269 for (
auto *mergeOp :
result->mergeOps)
3279void transform::TileReductionUsingForallOp::build(
3280 OpBuilder &builder, OperationState &
result, Value
target,
3281 ArrayRef<int64_t> staticNumThreads, ArrayRef<int64_t> staticTileSizes,
3289 auto opTy = transform::AnyOpType::get(ctx);
3296 staticNumThreadsAttr,
3297 staticTileSizesAttr,
3301DiagnosedSilenceableFailure transform::TileReductionUsingForallOp::applyToOne(
3302 transform::TransformRewriter &rewriter, Operation *
target,
3303 transform::ApplyToEachResultList &results,
3304 transform::TransformState &state) {
3307 auto partialReductionOp = dyn_cast<PartialReductionOpInterface>(
target);
3308 if (!partialReductionOp) {
3311 "Operation should implement PartialReductionOpInterface");
3313 SmallVector<OpFoldResult> numThreads =
3315 SmallVector<OpFoldResult> tileSizes =
3318 scf::SCFTilingOptions
options;
3319 options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
3320 options.setReductionTilingStrategy(
3322 if (!getNumThreads().empty()) {
3323 options.setNumThreads(numThreads);
3325 options.setTileSizes(tileSizes);
3327 if (
auto mapping = getMapping()) {
3328 options.setMapping(mapping.value().getValue());
3330 SmallVector<unsigned> reductionDims =
3332 if (reductionDims.empty()) {
3333 for (
auto [idx, iteratorType] :
3334 llvm::enumerate(partialReductionOp.getLoopIteratorTypes())) {
3335 if (iteratorType == utils::IteratorType::reduction)
3336 reductionDims.push_back(idx);
3339 options.setReductionDims(reductionDims);
3340 FailureOr<scf::SCFTilingResult>
result =
3341 scf::tileUsingSCF(rewriter, partialReductionOp,
options);
3344 auto diag = emitSilenceableError() <<
"could not tile reduction";
3349 for (Value initValue :
result->initialValues)
3351 for (
auto *parallelTiledOp :
result->tiledOps)
3353 for (
auto *mergeOp :
result->mergeOps)
3363DiagnosedSilenceableFailure
3364transform::ContinuousTileSizesOp::apply(transform::TransformRewriter &rewriter,
3365 TransformResults &transformResults,
3366 TransformState &state) {
3368 SmallVector<Operation *> targetOps =
3371 if (!llvm::hasSingleElement(targetOps)) {
3373 <<
"requires exactly one target (got " << llvm::range_size(targetOps)
3377 Operation *
target = *targetOps.begin();
3378 auto linalgOp = dyn_cast<LinalgOp>(
target);
3379 auto tileableOp = dyn_cast<TilingInterface>(
target);
3384 OpBuilder builder(linalgOp.getContext());
3386 if (isa<TransformParamTypeInterface>(getChunkSizes().
getType())) {
3387 if (linalgOp.hasDynamicShape()) {
3388 auto diag = emitSilenceableError()
3389 <<
"cannot compute parametric tile sizes for dynamically "
3390 "shaped payload op";
3391 diag.
attachNote(linalgOp->getLoc()) <<
"payload op";
3395 FailureOr<StaticContinuousTileSizeSpecification> spec =
3399 return emitSilenceableError()
3400 <<
"failed to compute multi-size tiling sizes";
3403 SmallVector<int64_t> chunkSizes;
3405 for (
auto &&[tileSize, tripCount] :
3406 llvm::zip_equal(spec->tileSizes, spec->tripCounts))
3407 chunkSizes.push_back(tileSize * tripCount);
3409 auto getI64AttrsFromI64 = [&](ArrayRef<int64_t> values) {
3410 return llvm::map_to_vector(values, [&](int64_t value) -> Attribute {
3415 getI64AttrsFromI64(spec->tileSizes));
3416 transformResults.
setParams(cast<OpResult>(getChunkSizes()),
3417 getI64AttrsFromI64(chunkSizes));
3424 OpFoldResult targetSize = builder.
getIndexAttr(getTargetSize());
3425 unsigned dimension = getDimension();
3428 builder, tileableOp, dimension, targetSize,
true);
3430 return emitSilenceableError() <<
"could not generate tile size computation";
3435 auto apply = [&](AffineExpr expr, ArrayRef<OpFoldResult> ofrs) -> Value {
3440 SmallVector<Value> chunkSizes;
3442 for (
auto &&[tileSize, tripCount] :
3443 llvm::zip_equal(spec->tileSizes, spec->tripCounts)) {
3444 splitPoint = apply(s0 * s1, {tileSize, tripCount});
3445 chunkSizes.push_back(splitPoint);
3448 auto getDefiningOps = [&](ArrayRef<Value> values) {
3449 return llvm::map_to_vector(values, [&](Value value) -> Operation * {
3455 getDefiningOps(spec->tileSizes));
3456 transformResults.
set(cast<OpResult>(getChunkSizes()),
3457 getDefiningOps(chunkSizes));
3462LogicalResult transform::ContinuousTileSizesOp::verify() {
3465 return emitOpError() <<
"expects all results type to be the same";
3471void transform::ContinuousTileSizesOp::getEffects(
3472 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3489 Type &tileSizesType,
3490 Type &chunkSizesType) {
3491 FunctionType funcType;
3493 if (failed(parser.
parseType<FunctionType>(funcType)))
3496 if (funcType.getNumInputs() != 1 || funcType.getNumResults() != 1) {
3497 parser.
emitError(typeLoc) <<
"expects a trailing functional type with one "
3498 "argument and one result";
3500 targetType = funcType.getInput(0);
3501 tileSizesType = chunkSizesType = funcType.getResult(0);
3510void transform::TileUsingForOp::build(
3512 Value
target, ArrayRef<int64_t> staticTileSizes,
3513 ArrayRef<int64_t> interchange,
3514 std::optional<ArrayRef<bool>> scalableSizes) {
3515 return build(builder,
result, loopTypes,
3519 interchange, scalableSizes);
3522void transform::TileUsingForOp::build(
3523 OpBuilder &builder, OperationState &
result, Value
target,
3524 ArrayRef<int64_t> staticTileSizes, ArrayRef<int64_t> interchange,
3525 std::optional<ArrayRef<bool>> scalableSizes) {
3528 interchange, scalableSizes);
3531void transform::TileUsingForOp::build(
3532 OpBuilder &builder, OperationState &
result, Value
target,
3533 ArrayRef<OpFoldResult> mixedTileSizes, ArrayRef<int64_t> interchange,
3534 std::optional<ArrayRef<bool>> scalableSizes) {
3537 SmallVector<Type> loopTypes(1, builder.
getType<transform::AnyOpType>());
3538 build(builder,
result, loopTypes,
target, mixedTileSizes, interchange,
3542void transform::TileUsingForOp::build(
3543 OpBuilder &builder, OperationState &
result, Value
target,
3544 ArrayRef<OpFoldResult> mixedTileSizes,
3545 ArrayRef<OpFoldResult> mixedInterchange,
3546 std::optional<ArrayRef<bool>> scalableSizes) {
3549 SmallVector<Type> loopTypes(1, builder.
getType<transform::AnyOpType>());
3550 build(builder,
result, loopTypes,
target, mixedTileSizes, mixedInterchange,
3554void transform::TileUsingForOp::build(
3556 Value
target, ArrayRef<OpFoldResult> mixedTileSizes,
3557 ArrayRef<int64_t> interchange,
3558 std::optional<ArrayRef<bool>> scalableSizes) {
3559 SmallVector<OpFoldResult> mixedInterchange =
3561 build(builder,
result, loopTypes,
target, mixedTileSizes, mixedInterchange,
3565void transform::TileUsingForOp::build(
3567 Value
target, ArrayRef<OpFoldResult> mixedTileSizes,
3568 ArrayRef<OpFoldResult> mixedInterchange,
3569 std::optional<ArrayRef<bool>> scalableSizes) {
3570 SmallVector<int64_t> staticTileSizes;
3571 SmallVector<Value> dynamicTileSizes;
3572 SmallVector<int64_t> staticInterchange;
3573 SmallVector<Value> dynamicInterchange;
3582 unsigned numExpectedLoops =
3583 staticTileSizes.size() - llvm::count(staticTileSizes, 0);
3584 SmallVector<Type> resultTypes;
3585 resultTypes.reserve(numExpectedLoops);
3586 assert((loopTypes.size() == 1 || loopTypes.size() == numExpectedLoops) &&
3587 "expected one loop type or as many as loops");
3588 if (loopTypes.size() == 1)
3589 resultTypes.append(numExpectedLoops, loopTypes[0]);
3591 llvm::append_range(resultTypes, loopTypes);
3592 SmallVector<bool> expandedScalableSizes(mixedTileSizes.size(),
false);
3593 if (scalableSizes.has_value())
3594 expandedScalableSizes.assign(scalableSizes->begin(), scalableSizes->end());
3595 Value packedTileSizes;
3603 staticTileSizesAttr,
3604 staticInterchangeAttr,
3605 expandedScalableSizes);
3608LogicalResult transform::TileUsingForOp::verify() {
3609 bool hasPackedTiles = getPackedTileSizes() != Value();
3610 bool hasPackedInterchange = getPackedInterchange() != Value();
3613 "tile_sizes and packed_tile_sizes are mutually exclusive");
3614 if (!getMixedInterchange().empty() && hasPackedInterchange)
3616 "interchange and packed_interchange are mutually exclusive");
3617 if (hasPackedTiles && !getScalableSizes().empty())
3619 "scalable tile_sizes are not supported with packed_tile_sizes");
3622 return emitOpError(
"expected same number of sizes (")
3624 << getScalableSizes().size() <<
")";
3626 auto iterspaceRank = getStaticSizes().size();
3627 ArrayRef<int64_t> permutation = getStaticInterchange();
3628 if (permutation.size() > iterspaceRank)
3629 return emitOpError()
3630 <<
"interchange length exceeds iteration space dimensions ("
3631 << iterspaceRank <<
"), found " << getInterchange();
3632 SmallVector<bool> seen(iterspaceRank,
false);
3633 for (int64_t v : permutation) {
3634 if (!ShapedType::isDynamic(v)) {
3635 if (v < 0 || v >=
static_cast<int64_t
>(iterspaceRank))
3636 return emitOpError() <<
"expects interchange values to be in range [0, "
3637 << iterspaceRank <<
"), found: " << v;
3639 return emitOpError() <<
"found duplicate interchange value: " << v;
3644 ArrayRef<int64_t> staticSizes = getStaticSizes();
3645 unsigned numExpectedLoops =
3646 hasPackedTiles ? 1 : staticSizes.size() - llvm::count(staticSizes, 0);
3647 if (getLoops().size() != numExpectedLoops)
3648 return emitOpError(
"expected number of loops to tile (")
3649 << numExpectedLoops <<
") to match number of `loops` results ("
3650 << getLoops().size() <<
")";
3654DiagnosedSilenceableFailure
3655transform::TileUsingForOp::apply(transform::TransformRewriter &rewriter,
3656 TransformResults &transformResults,
3657 TransformState &state) {
3658 ArrayRef<int64_t> tileSizes = getStaticSizes();
3659 bool hasPackedTiles = getPackedTileSizes() != Value();
3660 bool hasPackedInterchange = getPackedInterchange() != Value();
3661 auto transformOp = cast<TransformOpInterface>(getOperation());
3663 SmallVector<OpFoldResult> mixedInterchange;
3664 if (hasPackedInterchange) {
3665 DiagnosedSilenceableFailure status =
3667 state, transformOp, mixedInterchange, getPackedInterchange());
3671 mixedInterchange = getMixedInterchange();
3673 SmallVector<int64_t> tileInterchange;
3675 state, transformOp, mixedInterchange, tileInterchange);
3679 SmallVector<Operation *> targets =
3681 SmallVector<SmallVector<Operation *>> dynamicSizeProducers;
3682 SmallVector<SmallVector<int64_t>> paramSizes;
3683 SmallVector<OpFoldResult> mixedTileSizes;
3684 if (hasPackedTiles) {
3686 state, transformOp, mixedTileSizes, getPackedTileSizes());
3694 if (isa<TransformParamTypeInterface>(transformValue.getType())) {
3695 dynamicSizeProducers.push_back({});
3696 ArrayRef<Attribute> params = state.
getParams(transformValue);
3697 paramSizes.push_back(llvm::map_to_vector(params, [](Attribute attr) {
3698 return cast<IntegerAttr>(attr).getValue().getSExtValue();
3701 if (paramSizes.back().size() != targets.size()) {
3702 DiagnosedSilenceableFailure diag =
3703 emitSilenceableError()
3704 <<
"expected as many parameter values ("
3705 << dynamicSizeProducers.back().size() <<
") as target ops ("
3706 << targets.size() <<
")";
3707 diag.
attachNote(transformValue.getLoc()) <<
"for this parameter";
3713 paramSizes.push_back({});
3714 dynamicSizeProducers.push_back(
3717 if (dynamicSizeProducers.back().size() != targets.size()) {
3718 DiagnosedSilenceableFailure diag =
3719 emitSilenceableError()
3720 <<
"expected as many dynamic size-producing operations ("
3721 << dynamicSizeProducers.back().size() <<
") as target ops ("
3722 << targets.size() <<
")";
3723 diag.
attachNote(transformValue.getLoc()) <<
"for this handle";
3727 for (Operation *op : dynamicSizeProducers.back()) {
3733 DiagnosedSilenceableFailure diag =
3734 emitSilenceableError() <<
"expected sizes to be produced by ops "
3735 "with a single index-type result";
3737 diag.
attachNote(transformValue.getLoc()) <<
"for this handle";
3743 SmallVector<Operation *> tiled;
3744 SmallVector<SmallVector<Operation *, 4>, 4> loops;
3747 ? llvm::count_if(mixedTileSizes,
3748 [](OpFoldResult ofr) {
3749 if (
auto attr = dyn_cast<Attribute>(ofr))
3750 return cast<IntegerAttr>(attr).getInt() != 0;
3753 : getLoops().size();
3754 loops.resize(numLoops);
3755 auto scalableSizes = getScalableSizes();
3756 for (
auto [i, op] : llvm::enumerate(targets)) {
3757 auto tilingInterface = dyn_cast<TilingInterface>(op);
3758 if (!tilingInterface) {
3759 DiagnosedSilenceableFailure diag =
3760 emitSilenceableError()
3761 <<
"only ops implementing TilingInterface are supported";
3765 if (isa<linalg::RelayoutOpInterface>(op) &&
3766 !cast<DestinationStyleOpInterface>(op).hasPureTensorSemantics()) {
3767 DiagnosedSilenceableFailure diag =
3768 emitSilenceableError()
3769 <<
"tiling only supports tensor semantics for linalg.pack / "
3770 "linalg.unpack. The target has memref operands";
3775 int64_t iterspaceRank = tilingInterface.getLoopIteratorTypes().size();
3776 if (tileInterchange.size() >
static_cast<size_t>(iterspaceRank)) {
3777 return emitSilenceableError()
3778 <<
"interchange length exceeds iteration space dimensions ("
3779 << iterspaceRank <<
")";
3781 SmallVector<bool> seen(iterspaceRank,
false);
3782 for (int64_t v : tileInterchange) {
3783 if (v < 0 || v >= iterspaceRank) {
3784 return emitSilenceableError()
3785 <<
"expects interchange values to be in range [0, "
3786 << iterspaceRank <<
"), found: " << v;
3789 return emitSilenceableError()
3790 <<
"found duplicate interchange value: " << v;
3795 if (tileSizes.size() > tilingInterface.getLoopIteratorTypes().size()) {
3796 DiagnosedSilenceableFailure diag =
3797 emitSilenceableError()
3798 <<
"too many tiles provided, expected at most "
3799 << tilingInterface.getLoopIteratorTypes().size() <<
" found "
3800 << tileSizes.size();
3805 scf::SCFTilingOptions tilingOptions;
3806 if (!hasPackedTiles && tileSizes.empty()) {
3807 tilingOptions.setTileSizeComputationFunction(
3808 [](OpBuilder &, Operation *) -> SmallVector<OpFoldResult> {
3811 }
else if (hasPackedTiles) {
3812 tilingOptions.setTileSizes(mixedTileSizes);
3814 tilingOptions.setTileSizeComputationFunction([&, index = i](OpBuilder &
b,
3816 SmallVector<OpFoldResult> sizes;
3817 sizes.reserve(tileSizes.size());
3818 unsigned dynamicIdx = 0;
3820 for (
auto [ofrIdx, ofr] : llvm::enumerate(
getMixedSizes())) {
3821 if (
auto attr = llvm::dyn_cast_if_present<Attribute>(ofr)) {
3822 if (scalableSizes[ofrIdx]) {
3824 b, getLoc(), cast<IntegerAttr>(attr).getInt());
3826 vector::VectorScaleOp::create(
b, getLoc(),
b.getIndexType());
3828 arith::MulIOp::create(
b, getLoc(), val, vscale).getResult());
3830 sizes.push_back(attr);
3834 ArrayRef<Operation *> dynamicSizes = dynamicSizeProducers[dynamicIdx];
3835 ArrayRef<int64_t> params = paramSizes[dynamicIdx];
3837 assert((dynamicSizes.empty() ^ params.empty()) &&
3838 "expected either dynamic sizes or parameters");
3839 if (!params.empty()) {
3840 sizes.push_back(
b.getIndexAttr(params[index]));
3842 sizes.push_back(dynamicSizes[index]->getResult(0));
3849 tilingOptions.setInterchange(tileInterchange);
3850 tilingOptions.setInnerTileAlignments(
3852 FailureOr<scf::SCFTilingResult> maybeTilingResult =
3853 tileUsingSCF(rewriter, tilingInterface, tilingOptions);
3854 if (
failed(maybeTilingResult))
3857 rewriter.
replaceOp(op, maybeTilingResult->replacements);
3859 tiled.append(maybeTilingResult->tiledOps);
3860 for (
const auto &en2 : llvm::enumerate(maybeTilingResult->loops))
3861 loops[en2.index()].push_back(en2.value());
3864 transformResults.
set(cast<OpResult>(getTiledLinalgOp()), tiled);
3865 if (hasPackedTiles) {
3867 SmallVector<Operation *> flattenedLoops;
3868 for (
auto [targetIdx, _] : llvm::enumerate(targets))
3869 for (
auto [loopIdx, __] : llvm::enumerate(loops))
3870 flattenedLoops.push_back(loops[loopIdx][targetIdx]);
3871 transformResults.
set(cast<OpResult>(getLoops().front()), flattenedLoops);
3873 for (
const auto &en : llvm::enumerate(loops))
3874 transformResults.
set(cast<OpResult>(getLoops()[en.index()]), en.value());
3880SmallVector<OpFoldResult> transform::TileUsingForOp::getMixedSizes() {
3882 ArrayRef<int64_t> tileSizes = getStaticSizes();
3883 SmallVector<OpFoldResult> results;
3884 results.reserve(tileSizes.size());
3885 unsigned dynamicPos = 0;
3887 for (int64_t size : tileSizes) {
3888 if (size == ShapedType::kDynamic) {
3889 results.push_back(dynamic[dynamicPos++]);
3897SmallVector<OpFoldResult> transform::TileUsingForOp::getMixedInterchange() {
3901void transform::TileUsingForOp::getEffects(
3902 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3916void transform::TileUsingForallOp::build(OpBuilder &builder,
3918 ArrayRef<int64_t> staticTileSizes,
3919 transform::TileSizesSpec,
3921 return build(builder,
result,
3929void transform::TileUsingForallOp::build(OpBuilder &builder,
3931 ArrayRef<OpFoldResult> mixedTileSizes,
3932 transform::TileSizesSpec,
3934 SmallVector<int64_t> staticTileSizes;
3935 SmallVector<Value> dynamicTileSizes;
3941 auto operationType = transform::AnyOpType::get(ctx);
3944 TypeRange{operationType, operationType},
3951 staticTileSizesAttr,
3955void transform::TileUsingForallOp::build(OpBuilder &builder,
3957 ArrayRef<int64_t> staticNumThreads,
3958 transform::NumThreadsSpec,
3962 NumThreadsSpec(), mapping);
3965void transform::TileUsingForallOp::build(OpBuilder &builder,
3967 ArrayRef<OpFoldResult> mixedNumThreads,
3968 transform::NumThreadsSpec,
3970 SmallVector<int64_t> staticNumThreads;
3971 SmallVector<Value> dynamicNumThreads;
3978 auto operationType = transform::AnyOpType::get(ctx);
3981 TypeRange{operationType, operationType},
3987 staticNumThreadsAttr,
3994static SmallVector<OpFoldResult>
4000 AffineExpr normalizedUbExpr = (s1 - s0).ceilDiv(s2);
4002 for (
auto [lb,
ub, step] : llvm::zip_equal(lbs, ubs, steps)) {
4004 rewriter, loc, normalizedUbExpr, {lb,
ub, step});
4005 normalizedUbs.push_back(normalizedUb);
4007 return normalizedUbs;
4023 for (
auto [iv, lb, step] : llvm::zip_equal(ivs, lbs, steps)) {
4026 denormalizedIvs.push_back(
4029 return denormalizedIvs;
4040 scf::ForallOp loop) {
4057 auto normalizedForallOp = scf::ForallOp::create(
4058 rewriter, loc, normalizedLbs, normalizedUbs, normalizedSteps,
4059 loop.getOutputs(), loop.getMapping(),
4062 auto normalizedLoopIvs = normalizedForallOp.getInductionVars();
4064 Block *normalizedLoopBlock = normalizedForallOp.getBody();
4069 argValues.append(normalizedForallOp.getRegionIterArgs().begin(),
4070 normalizedForallOp.getRegionIterArgs().end());
4071 Block *origLoopBlock = loop.getBody();
4072 rewriter.
mergeBlocks(origLoopBlock, normalizedLoopBlock, argValues);
4074 rewriter.
replaceOp(loop, normalizedForallOp);
4075 return normalizedForallOp;
4083 scf::SCFTilingResult &tilingResult) {
4085 auto tileableOp = dyn_cast<TilingInterface>(
target);
4088 transformOp.emitSilenceableError()
4089 <<
"only TilingInterface ops are supported";
4090 diag.attachNote(
target->getLoc()) <<
"target op";
4093 if (isa<linalg::RelayoutOpInterface>(
target) &&
4094 !cast<DestinationStyleOpInterface>(
target).hasPureTensorSemantics()) {
4096 transformOp.emitSilenceableError()
4097 <<
"tiling only supports tensor semantics for linalg.pack / "
4098 "linalg.unpack. The target has memref operands";
4099 diag.attachNote(
target->getLoc()) <<
"target op";
4103 scf::SCFTilingOptions
options;
4104 options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
4105 if (!mixedNumThreads.empty()) {
4106 options.setNumThreads(mixedNumThreads);
4108 options.setTileSizes(mixedTileSizes);
4111 options.setMapping(mapping.value().getValue());
4113 FailureOr<scf::SCFTilingResult> maybeTilingResult =
4114 scf::tileUsingSCF(rewriter, tileableOp,
options);
4116 if (failed(maybeTilingResult))
4117 return transformOp.emitDefaultSilenceableFailure(tileableOp);
4119 rewriter.
replaceOp(tileableOp, maybeTilingResult->replacements);
4121 tilingResult = *maybeTilingResult;
4125 if (mixedNumThreads.empty() && !tilingResult.loops.empty()) {
4126 auto generatedForallOp = cast<scf::ForallOp>(tilingResult.loops.front());
4129 scf::ForallOp normalizedForallOp =
4131 tilingResult.loops.front() = normalizedForallOp;
4141 auto transformOp = cast<TransformOpInterface>(getOperation());
4150 getPackedNumThreads()
4152 state, transformOp, mixedNumThreads, getPackedNumThreads())
4154 state, transformOp, mixedNumThreads, getMixedNumThreads());
4158 status = getPackedTileSizes()
4160 state, transformOp, mixedTileSizes, getPackedTileSizes())
4162 state, transformOp, mixedTileSizes, getMixedTileSizes());
4167 scf::SCFTilingResult tilingResult;
4169 rewriter, state, transformOp,
target, mixedNumThreads, mixedTileSizes,
4170 getMapping(), tilingResult);
4171 if (!
diag.succeeded())
4173 if (!tilingResult.loops.empty())
4174 tileOps.push_back(tilingResult.loops.front());
4175 tiledOps.append(tilingResult.tiledOps);
4178 transformResults.
set(cast<OpResult>(getForallOp()), tileOps);
4179 transformResults.
set(cast<OpResult>(getTiledOp()), tiledOps);
4184void transform::TileUsingForallOp::getEffects(
4185 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
4195SmallVector<OpFoldResult> TileUsingForallOp::getMixedNumThreads() {
4200SmallVector<OpFoldResult> TileUsingForallOp::getMixedTileSizes() {
4205LogicalResult TileUsingForallOp::verify() {
4206 int numThreadsSpec =
static_cast<int>(!getMixedNumThreads().empty()) +
4207 static_cast<int>(getPackedNumThreads() != Value());
4208 if (numThreadsSpec > 1)
4210 "num_threads and packed_num_threads are mutually exclusive");
4211 int tileSizesSpec =
static_cast<int>(!getMixedTileSizes().empty()) +
4212 static_cast<int>(getPackedTileSizes() != Value());
4213 if (tileSizesSpec > 1)
4215 "tile_sizes and packed_tile_sizes are mutually exclusive");
4216 if (numThreadsSpec == 0 && tileSizesSpec == 0)
4217 return emitOpError(
"either (packed_)num_threads or (packed_)tile_sizes "
4218 "must be specified");
4226void transform::VectorizeChildrenAndApplyPatternsOp::build(
4227 OpBuilder &builder, OperationState &
result, Value
target,
4228 bool foldTypeExtensionsIntoContract,
bool vectorizePadding,
4229 bool vectorizeExtract,
bool flatten1DDepthwiseConv) {
4231 if (foldTypeExtensionsIntoContract) {
4233 VectorizeChildrenAndApplyPatternsOp::
4234 getFoldTypeExtensionsIntoContractAttrName(
result.name),
4237 if (vectorizePadding) {
4239 VectorizeChildrenAndApplyPatternsOp::getVectorizePaddingAttrName(
4243 if (vectorizeExtract) {
4245 VectorizeChildrenAndApplyPatternsOp::getVectorizeNdExtractAttrName(
4249 if (flatten1DDepthwiseConv) {
4251 VectorizeChildrenAndApplyPatternsOp::getFlatten_1dDepthwiseConvAttrName(
4261struct VectorizationPattern :
public RewritePattern {
4262 explicit VectorizationPattern(MLIRContext *context,
4263 bool vectorizeExtract =
false,
4264 bool flattenConv =
false)
4265 : RewritePattern(MatchAnyOpTypeTag(), 1, context),
4266 vectorizeNDExtract(vectorizeExtract),
4267 flatten1DDepthwiseConv(flattenConv) {}
4268 LogicalResult matchAndRewrite(Operation *op,
4269 PatternRewriter &rewriter)
const override {
4272 "Unsupported Op, cannot vectorize");
4273 FailureOr<VectorizationResult> vectorResults =
4275 {}, vectorizeNDExtract,
4276 flatten1DDepthwiseConv);
4277 if (
failed(vectorResults))
4279 rewriter.
replaceOp(op, vectorResults->replacements);
4286 bool vectorizeNDExtract =
false;
4290 bool flatten1DDepthwiseConv =
false;
4294DiagnosedSilenceableFailure
4295transform::VectorizeChildrenAndApplyPatternsOp::applyToOne(
4296 transform::TransformRewriter &rewriter, Operation *
target,
4297 transform::ApplyToEachResultList &results,
4298 transform::TransformState &state) {
4299 if (!
target->hasTrait<OpTrait::IsIsolatedFromAbove>()) {
4300 auto diag = this->emitOpError(
"requires isolated-from-above targets");
4306 RewritePatternSet patterns(ctx);
4307 patterns.
add<VectorizationPattern>(ctx, getVectorizeNdExtract(),
4308 getFlatten_1dDepthwiseConv());
4310 if (!getDisableTransferPermutationMapLoweringPatterns())
4313 if (!getDisableMultiReductionToContractPatterns())
4318 patterns.
add<linalg::LinalgCopyVTRForwardingPattern,
4319 linalg::LinalgCopyVTWForwardingPattern>(ctx,
4321 vector::TransferReadOp::getCanonicalizationPatterns(patterns, ctx);
4322 vector::TransferWriteOp::getCanonicalizationPatterns(patterns, ctx);
4325 patterns.
add<CopyVectorizationPattern>(ctx);
4327 if (getFoldTypeExtensionsIntoContract())
4330 if (getVectorizePadding()) {
4338 TrackingListener listener(state, *
this);
4341 GreedyRewriteConfig().setListener(&listener))))
4342 return emitDefaultDefiniteFailure(
target);
4352DiagnosedSilenceableFailure transform::VectorizeOp::apply(
4353 transform::TransformRewriter &rewriter,
4354 mlir::transform::TransformResults &transformResults,
4355 mlir::transform::TransformState &state) {
4357 if (std::empty(targets))
4359 auto transformOp = cast<TransformOpInterface>(getOperation());
4360 SmallVector<int64_t> vectorSizes;
4362 state, transformOp, getMixedVectorSizes(), vectorSizes);
4367 for (Operation *
target : targets) {
4370 <<
"Unsupported Op, cannot vectorize";
4372 FailureOr<VectorizationResult> vectorResults =
4374 getVectorizeNdExtract().value_or(
false),
4376 getAssumeDynamicDimsMatchVecSizes().value_or(
false),
4377 getCreateNamedContraction().value_or(
false));
4378 if (
failed(vectorResults)) {
4380 <<
"Attempted to vectorize, but failed";
4388void transform::VectorizeOp::getEffects(
4389 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
4395SmallVector<OpFoldResult> VectorizeOp::getMixedVectorSizes() {
4400LogicalResult transform::VectorizeOp::verify() {
4401 if (getStaticVectorSizes().size() != getScalableSizes().size())
4402 return emitOpError(
"expected same number of vector sizes (")
4403 << getStaticVectorSizes().size() <<
") and scalable sizes ("
4404 << getScalableSizes().size() <<
")";
4412DiagnosedSilenceableFailure
4413transform::HoistRedundantVectorTransfersOp::applyToOne(
4414 transform::TransformRewriter &rewriter, func::FuncOp
target,
4415 transform::ApplyToEachResultList &results,
4416 transform::TransformState &state) {
4429DiagnosedSilenceableFailure
4430transform::HoistRedundantVectorBroadcastsOp::applyToOne(
4431 transform::TransformRewriter &rewriter, mlir::Operation *
target,
4432 transform::ApplyToEachResultList &results,
4433 transform::TransformState &state) {
4444DiagnosedSilenceableFailure transform::ConvertConv2DToImg2ColOp::applyToOne(
4445 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4446 transform::ApplyToEachResultList &results,
4447 transform::TransformState &state) {
4449 auto maybeTransformed =
4452 .Case([&](linalg::Conv2DNhwcHwcfOp op) {
4455 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4458 .Case([&](linalg::DepthwiseConv2DNhwcHwcOp op) {
4461 .Case([&](linalg::Conv2DNchwFchwOp op) {
4464 .Default([&](Operation *op) {
4467 if (
failed(maybeTransformed))
4468 return emitDefaultSilenceableFailure(
target);
4470 results.
push_back(maybeTransformed->first);
4472 results.
push_back(maybeTransformed->second);
4480DiagnosedSilenceableFailure transform::FlattenElementwiseLinalgOp::applyToOne(
4481 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4482 transform::ApplyToEachResultList &results,
4483 transform::TransformState &state) {
4487 <<
"only elementwise flattening is supported";
4489 if (!llvm::all_of(
target.getIndexingMapsArray(), [](AffineMap m) {
4490 return m.isPermutation() || m.getNumResults() == 0;
4494 <<
"broadcasting of non scalar operands is not supported";
4498 if (
target.getNumLoops() <= 1) {
4505 if (
auto broadcastOp = dyn_cast<linalg::BroadcastOp>(
target.getOperation());
4506 broadcastOp && broadcastOp.getInput().getType().getRank() != 0) {
4513 std::iota(reassociation.begin(), reassociation.end(), 0);
4514 auto maybeFlattened =
4516 if (
failed(maybeFlattened))
4518 <<
"attempted to flatten, but failed";
4519 results.
push_back(maybeFlattened->collapsedOp);
4528DiagnosedSilenceableFailure transform::TransposeConv2DOp::applyToOne(
4529 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4530 transform::ApplyToEachResultList &results,
4531 transform::TransformState &state) {
4533 auto maybeTransformed =
4535 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4538 .Case([&](linalg::Conv2DNhwcFhwcQOp op) {
4541 .Default([&](Operation *op) {
4544 if (
failed(maybeTransformed))
4545 return emitDefaultSilenceableFailure(
target);
4555DiagnosedSilenceableFailure transform::TransposeMatmulOp::applyToOne(
4556 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4557 transform::ApplyToEachResultList &results,
4558 transform::TransformState &state) {
4560 bool transposeLHS = getInputToTranspose() == TransposeMatmulInput::lhs;
4561 auto maybeTransformed =
4563 .Case([&](linalg::MatmulOp op) {
4566 .Case([&](linalg::BatchMatmulOp op) {
4569 .Default(failure());
4570 if (
failed(maybeTransformed))
4580template <
typename OpTy>
4581static DiagnosedSilenceableFailure
4585 static_assert(llvm::is_one_of<OpTy, tensor::InsertSliceOp,
4586 tensor::ParallelInsertSliceOp>() &&
4589 if (
auto copySource =
4590 target.getSource().template getDefiningOp<linalg::CopyOp>()) {
4598 if (isa<mlir::ParallelCombiningOpInterface>(
target.getOperation()))
4601 Value extracted = tensor::ExtractSliceOp::create(
4604 Value copied = linalg::CopyOp::create(rewriter,
target.getLoc(),
4605 target.getSource(), extracted)
4617DiagnosedSilenceableFailure transform::InsertSliceToCopyOp::applyToOne(
4618 transform::TransformRewriter &rewriter, Operation *targetOp,
4619 transform::ApplyToEachResultList &results,
4620 transform::TransformState &state) {
4623 if (
auto target = dyn_cast<tensor::InsertSliceOp>(targetOp))
4624 return doit(rewriter,
target, results, state);
4625 if (
auto target = dyn_cast<tensor::ParallelInsertSliceOp>(targetOp))
4626 return doit(rewriter,
target, results, state);
4628 DiagnosedSilenceableFailure diag =
4629 emitSilenceableError()
4630 <<
"only InsertSliceOp and ParallelInsertSliceOp ops are supported";
4639DiagnosedSilenceableFailure transform::MapCopyToThreadsOp::applyToOne(
4640 transform::TransformRewriter &rewriter, Operation *
target,
4641 transform::ApplyToEachResultList &results,
4642 transform::TransformState &state) {
4644 if (!isa<linalg::CopyOp, tensor::PadOp>(
target)) {
4645 DiagnosedSilenceableFailure diag =
4646 emitSilenceableError()
4647 <<
"only linalg.copy and tensor.pad target ops are supported";
4651 assert(
target->getNumResults() == 1 &&
"expected single result");
4652 auto resultShapedType = cast<ShapedType>(
target->getResult(0).getType());
4653 if (!resultShapedType.hasStaticShape()) {
4654 DiagnosedSilenceableFailure diag =
4655 emitSilenceableError()
4656 <<
"only statically sized ops of rank <= 3 are supported";
4662 int64_t desiredBitAlignment = getDesiredBitAlignment();
4663 int64_t eltBitwidth =
4664 resultShapedType.getElementType().getIntOrFloatBitWidth();
4665 if (desiredBitAlignment % eltBitwidth != 0) {
4666 desiredBitAlignment = eltBitwidth;
4669 gpu::CopyMappingInfo mapping(
4671 getTotalNumThreads(),
4672 desiredBitAlignment,
4673 resultShapedType.getShape(),
4676 resultShapedType.getElementType().getIntOrFloatBitWidth());
4677 if (mapping.status == gpu::CopyMappingInfo::Status::Invalid) {
4678 DiagnosedSilenceableFailure diag =
4679 emitSilenceableError()
4680 <<
"too few threads to map copy op to threads on the most minor "
4681 "dimension, given alignment and vector size constraints, try "
4682 "smaller tile size of mapping to more threads";
4689 scf::SCFTilingResult tilingResult;
4696 ArrayRef<OpFoldResult>{},
4697 b.getArrayAttr(mapping.threadMapping),
4702 results.
push_back(tilingResult.loops.front());
4703 for (
auto *op : tilingResult.tiledOps)
4712DiagnosedSilenceableFailure transform::WinogradConv2DOp::applyToOne(
4713 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4714 transform::ApplyToEachResultList &results,
4715 transform::TransformState &state) {
4717 FailureOr<Operation *> maybeTransformed = failure();
4719 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4724 .Default([&](Operation *op) {
return false; });
4727 return emitSilenceableError()
4728 <<
"this operation is not supported to convert to Winograd Conv2D";
4731 if (
failed(maybeTransformed)) {
4732 return emitSilenceableError() <<
"apply Winograd Conv2D failed";
4739DiagnosedSilenceableFailure transform::DecomposeWinogradOp::applyToOne(
4740 transform::TransformRewriter &rewriter, Operation *
target,
4741 transform::ApplyToEachResultList &results,
4742 transform::TransformState &state) {
4744 FailureOr<Operation *> maybeTransformed = failure();
4747 .Case([&](linalg::WinogradFilterTransformOp op) {
4751 .Case([&](linalg::WinogradInputTransformOp op) {
4755 .Case([&](linalg::WinogradOutputTransformOp op) {
4762 DiagnosedSilenceableFailure diag =
4763 emitSilenceableError()
4764 <<
"this operation is not supported to decompose into other operations";
4769 if (
failed(maybeTransformed)) {
4770 DiagnosedSilenceableFailure diag =
4771 emitSilenceableError() <<
"decompose Winograd operations failed";
4780#include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOpsEnums.cpp.inc"
4782#define GET_OP_CLASSES
4783#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`
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)
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.
Diagnostic & attachNote(std::optional< Location > loc=std::nullopt)
Attaches a note to the last diagnostic.
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.
bool isSilenceableFailure() const
Returns true if this is a silenceable failure.
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)
bool hasOneUse()
Returns true if this operation has exactly one use.
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.
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...
virtual bool 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.
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.
void populateEraseUnusedOperandsAndResultsPatterns(RewritePatternSet &patterns)
Pattern to remove dead operands and results of linalg.generic operations.
std::function< bool(OpOperand *fusedOperand)> ControlFusionFn
Function type which is used to control when to stop fusion.
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.
void populateFoldReshapeOpsByExpansionPatterns(RewritePatternSet &patterns, const ControlFusionFn &controlFoldingReshapes)
Patterns to fold an expanding (collapsing) tensor_reshape operation with its producer (consumer) gene...
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 populateVectorReductionToContractPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1, bool allowFastMathFlagLoss=true)
Collect patterns to convert reduction op to vector.contract and fold transpose/broadcast ops into the...
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 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.