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(
288 SmallVector<Operation *> getNewOps()
const {
289 return SmallVector<Operation *>(newOps.begin(), newOps.end());
293 void notifyOperationInserted(Operation *op,
294 OpBuilder::InsertPoint previous)
override {
295 ForwardingListener::notifyOperationInserted(op, previous);
297 if (previous.
isSet())
301 assert(
inserted.second &&
"expected newly created op");
304 void notifyOperationErased(Operation *op)
override {
305 ForwardingListener::notifyOperationErased(op);
306 op->
walk([&](Operation *op) { newOps.erase(op); });
318 llvm::scope_exit resetListener(
319 [&]() { rewriter.
setListener(previousListener); });
320 NewOpsListener newOpsListener(previousListener);
324 if (getMemcpyOp() ==
"bufferization.materialize_in_destination") {
325 options.memcpyOp = linalg::BufferizeToAllocationOptions::MemcpyOp::
326 MaterializeInDestination;
327 }
else if (getMemcpyOp() ==
"memref.copy") {
330 }
else if (getMemcpyOp() ==
"linalg.copy") {
334 llvm_unreachable(
"invalid memcpy op");
336 if (getAllocOp() ==
"memref.alloc") {
339 }
else if (getAllocOp() ==
"memref.alloca") {
343 llvm_unreachable(
"invalid alloc op");
345 options.bufferizeDestinationOnly = getBufferizeDestinationOnly();
346 options.emitDealloc = getEmitDealloc();
350 getMemorySpace().has_value() ? getMemorySpace().value() :
Attribute();
357 <<
"failed to bufferize operation";
358 diag.attachNote(op->
getLoc()) <<
"target payload op";
361 allocatedBuffers.push_back(buffer);
365 results.
setValues(cast<OpResult>(getAllocatedBuffer()), allocatedBuffers);
366 results.
set(cast<OpResult>(getNewOps()), newOpsListener.getNewOps());
370void transform::BufferizeToAllocationOp::getEffects(
372 if (getBufferizeDestinationOnly()) {
383LogicalResult transform::BufferizeToAllocationOp::verify() {
384 if (getMemcpyOp() !=
"bufferization.materialize_in_destination" &&
385 getMemcpyOp() !=
"memref.copy" && getMemcpyOp() !=
"linalg.copy")
386 return emitOpError() <<
"unsupported memcpy op";
387 if (getAllocOp() !=
"memref.alloc" && getAllocOp() !=
"memref.alloca")
388 return emitOpError() <<
"unsupported alloc op";
400 auto linalgOp = dyn_cast<linalg::LinalgOp>(operand.
getOwner());
407 Value blockArgument = linalgOp.getMatchingBlockArgument(&operand);
415 if (!isa<TensorType, FloatType, IntegerType>(value.
getType()))
417 return llvm::any_of(value.
getUses(),
427 auto type = dyn_cast<RankedTensorType>(
tensor.getType());
429 return emitSilenceableError() <<
"non-tensor type: " <<
tensor;
443 for (
auto [pos, dim] : llvm::enumerate(type.getShape())) {
444 if (!ShapedType::isDynamic(dim))
449 tensor::DimOp::create(rewriter,
tensor.getLoc(),
tensor, cst);
450 preservedOps.insert(dimOp);
451 dynamicDims.push_back(dimOp);
453 auto allocation = bufferization::AllocTensorOp::create(
454 rewriter,
tensor.getLoc(), type, dynamicDims);
456 if (getMemorySpaceAttr())
457 allocation.setMemorySpaceAttr(getMemorySpaceAttr());
458 Value allocated = allocation;
462 if (needsMaterialization) {
463 auto copy = bufferization::MaterializeInDestinationOp::create(
465 preservedOps.insert(
copy);
466 promoted.push_back(
copy.getResult());
468 promoted.push_back(allocated);
472 results.
setValues(cast<OpResult>(getPromoted()), promoted);
476void transform::PromoteTensorOp::getEffects(
492 FailureOr<linalg::LinalgOp> res =
494 if (succeeded(res)) {
498 return emitDefaultSilenceableFailure(
target);
512 auto decomposableOp = dyn_cast<AggregatedOpInterface>(
target);
513 if (!decomposableOp) {
515 "payload is not a decomposable op");
516 return emitDefaultSilenceableFailure(
target);
519 FailureOr<SmallVector<Value>> maybeNewResults =
520 decomposableOp.decomposeOperation(rewriter);
521 if (
failed(maybeNewResults))
522 return emitDefaultSilenceableFailure(
target);
524 rewriter.
replaceOp(decomposableOp, *maybeNewResults);
525 for (
Value val : *maybeNewResults) {
526 Operation *definition = val.getDefiningOp();
537void transform::EliminateLinalgOpAnchoredEmptyTensorsOp::getEffects(
544transform::EliminateLinalgOpAnchoredEmptyTensorsOp::apply(
548 options.allowReturnAllocsFromLoops =
true;
554 <<
"failed to analyze op";
556 rewriter,
target, state)))
558 <<
"failed to eliminate LinalgOp anchored tensor.empty ops";
571 bool applyCleanup,
bool useForall) {
573 builder,
result, loopTypes,
579 applyCleanup, useForall);
585 bool applyCleanup,
bool useForall) {
593 applyCleanup, useForall);
600 bool applyCleanup,
bool useForall) {
604 build(builder,
result, loopTypes,
target, mixedTileSizes,
605 mixedTileInterchange, applyCleanup, useForall);
612 bool applyCleanup,
bool useForall) {
619 staticTileInterchange);
624 auto staticTileInterchangeAttr =
626 unsigned numExpectedLoops =
627 useForall ? 1 : staticTileSizes.size() - llvm::count(staticTileSizes, 0);
629 resultTypes.reserve(numExpectedLoops);
630 assert((loopTypes.size() == 1 || loopTypes.size() == numExpectedLoops) &&
631 "expected one loop type or as many as loops");
632 if (loopTypes.size() == 1)
633 resultTypes.append(numExpectedLoops, loopTypes[0]);
635 llvm::append_range(resultTypes, loopTypes);
640 dynamicTileInterchange,
643 staticTileInterchangeAttr,
651template <
typename Range>
656 function_ref<FailureOr<scf::SCFTileAndFuseResult>(TilingInterface)>
660 size_t numTargets = llvm::range_size(payloadOps);
663 auto tilingInterfaceOp = dyn_cast<TilingInterface>(
target);
664 if (!tilingInterfaceOp)
665 return transformOp->
emitError(
"only TilingInterface ops are supported");
668 FailureOr<scf::SCFTileAndFuseResult> tiledResults =
669 applyFn(tilingInterfaceOp);
670 if (failed(tiledResults))
675 llvm::append_range(opsToReplace, tiledResults->fusedProducers);
676 for (
Operation *toReplace : opsToReplace) {
677 for (
OpResult res : toReplace->getResults())
678 if (
auto replacement = tiledResults->replacements.lookup(res))
680 if (toReplace->use_empty()) {
686 tiledLinalgOps.push_back(tiledResults->tiledAndFusedOps.front());
687 assert(tiledResults->loops.size() == numLoops &&
688 "Mismatched number of loops, tile and fuse transform should have "
690 for (
unsigned int i = 0; i < numLoops; ++i)
691 loopOps[i].
push_back(tiledResults->loops[i]);
694 transformResults.
set(transformOp->
getOpResult(0), tiledLinalgOps);
703 for (
unsigned int idx = 0; idx < numTargets; ++idx)
704 for (
unsigned int i = 0; i < numLoops; ++i)
705 flattenedLoopOps.push_back(loopOps[i][idx]);
706 transformResults.
set(transformOp->
getOpResult(1), flattenedLoopOps);
708 for (
unsigned int i = 0; i < numLoops; ++i)
709 transformResults.
set(transformOp->
getOpResult(i + 1), loopOps[i]);
719 auto transformOp = cast<TransformOpInterface>(getOperation());
725 state, transformOp, mixedTileSizes, getPackedTileSizes())
727 state, transformOp, mixedTileSizes, getMixedTileSizes());
732 state, transformOp, getMixedTileInterchange(), tileInterchange);
736 scf::SCFTilingOptions tilingOptions;
737 tilingOptions.interchangeVector = std::move(tileInterchange);
738 bool useForall = getUseForall();
739 tilingOptions.setLoopType(useForall
740 ? scf::SCFTilingOptions::LoopType::ForallOp
741 : scf::SCFTilingOptions::LoopType::ForOp);
742 tilingOptions.setTileSizes(mixedTileSizes);
743 scf::SCFTileAndFuseOptions tileAndFuseOptions;
744 tileAndFuseOptions.tilingOptions = std::move(tilingOptions);
747 tileAndFuseOptions.tilingOptions.setInnerTileAlignments(
750 if (getApplyCleanup()) {
753 tensor::ExtractSliceOp::getCanonicalizationPatterns(patterns, context);
756 tileAndFuseOptions.cleanupPatterns = std::move(patterns);
763 numLoops = llvm::count_if(mixedTileSizes, [](
OpFoldResult ofr) {
764 auto attr = dyn_cast<Attribute>(ofr);
767 return cast<IntegerAttr>(attr).getInt() != 0;
771 rewriter, getOperation(), state.
getPayloadOps(getTarget()), numLoops,
772 transformResults, getPackedTileSizes() !=
nullptr,
773 [&](TilingInterface tilingInterfaceOp)
774 -> FailureOr<scf::SCFTileAndFuseResult> {
782LogicalResult transform::FuseOp::verify() {
783 bool hasPackedTiles = getPackedTileSizes() !=
nullptr;
784 if (!getMixedTileSizes().empty() && hasPackedTiles)
786 "tile_sizes and packed_tile_sizes are mutually exclusive");
788 auto iterspace_rank = getStaticTileSizes().size();
790 if (permutation.size() > iterspace_rank)
792 <<
"interchange length exceeds iteration space dimensions ("
793 << iterspace_rank <<
"), found " << getTileInterchange();
795 for (
int64_t v : permutation) {
796 if (!ShapedType::isDynamic(v)) {
797 if (v < 0 || v >=
static_cast<int64_t>(iterspace_rank))
798 return emitOpError() <<
"expects interchange values to be in range [0, "
799 << iterspace_rank <<
"), found: " << v;
801 return emitOpError() <<
"found duplicate interchange value: " << v;
807 size_t numExpectedLoops = getUseForall() || hasPackedTiles
809 : sizes.size() - llvm::count(sizes, 0);
810 if (numExpectedLoops != getNumResults() - 1)
811 return emitOpError() <<
"expects " << numExpectedLoops <<
" loop results";
821 return getMixedValues(getStaticTileInterchange(), getTileInterchange(),
825void transform::FuseOp::getEffects(
839void transform::FuseIntoContainingOp::build(
OpBuilder &builder,
842 Value containingOp) {
843 result.addOperands({producerOp, containingOp});
844 auto resultType = transform::AnyOpType::get(builder.
getContext());
845 result.addTypes({resultType, resultType});
861 (domInfo.
dominates(containingOp, user))) {
862 dominatedUsers.insert(user);
865 if (dominatedUsers.empty())
869 auto forallOp = cast<scf::ForallOp>(containingOp);
875 auto genericOp = dyn_cast<linalg::GenericOp>(producerOp);
880 newOuts.push_back(outputs[resultNumber]);
883 auto newforallOp = scf::ForallOp::create(
884 rewriter, loc, forallOp.getMixedLowerBound(),
885 forallOp.getMixedUpperBound(), forallOp.getMixedStep(), newOuts,
886 forallOp.getMapping());
888 newforallOp.getRegion().takeBody(forallOp.getRegion());
893 newforallOp.getBody()->addArgument(newOuts.back().getType(),
894 newOuts.back().getLoc());
895 auto bbArgs = newforallOp.getBody()->getArguments();
898 Operation *op = use.getOwner();
899 return newforallOp->isProperAncestor(op);
903 scf::InParallelOp terminatorOp = newforallOp.getTerminator();
905 terminatorOp.getYieldingOps(), [](
Operation &op) { return &op; });
906 Operation *firstYieldOp = yieldingOps.front();
909 Value dst = newforallOp.getRegionIterArgs().back();
911 tensor::ParallelInsertSliceOp::create(rewriter, firstYieldOp->
getLoc(), src,
912 dst, offsets, sizes, strides);
914 for (
auto result : llvm::enumerate(forallOp.getResults())) {
916 newforallOp->getResult(
result.index()));
919 newforallOp->getResults().back(),
921 Operation *user = use.getOwner();
922 return dominatedUsers.contains(user);
936 destWorklist.push_back(dst);
938 while (!destWorklist.empty()) {
939 Value currentDst = destWorklist.pop_back_val();
943 if (src == currentDst)
948 auto bbArg = dyn_cast<BlockArgument>(currentDst);
952 Block *parentBlock = bbArg.getOwner();
953 assert(parentBlock &&
"unlinked block argument");
956 assert(parentOp &&
"expected block argument with parent operation");
959 auto parentLoop = dyn_cast<LoopLikeOpInterface>(parentOp);
963 for (
auto innerIterArg : parentLoop.getRegionIterArgs()) {
965 OpOperand *operand = parentLoop.getTiedLoopInit(innerIterArg);
966 Value loopBlockArgument =
968 destWorklist.push_back(loopBlockArgument);
981static std::tuple<SmallVector<Operation *>,
Operation *>
985 LDBG() <<
"Try to fuse a direct extract use";
986 auto tileableProducer = dyn_cast<TilingInterface>(producerOp);
987 if (!tileableProducer) {
989 <<
"producer is not a TileableInterface: " << *producerOp;
996 auto it = llvm::find_if(tileableProducer->getUsers(), [&](
Operation *user) {
997 auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user);
998 return sliceOp && containingOp->isProperAncestor(sliceOp);
1002 if (it == tileableProducer->getUsers().end()) {
1003 diag.attachNote(tileableProducer->getLoc())
1004 <<
"could not find fusion opportunity for: " << *tileableProducer;
1007 auto sliceOpToTile = cast<tensor::ExtractSliceOp>(*it);
1020 if (LoopLikeOpInterface containerLoop =
1021 dyn_cast<LoopLikeOpInterface>(sliceOpToTile->getParentOp())) {
1027 auto dpsInterface = dyn_cast<DestinationStyleOpInterface>(
clone);
1031 for (
OpOperand &initOperandPtr : dpsInterface.getDpsInitsMutable()) {
1032 Value producerOperand =
1033 clone->getOperand(initOperandPtr.getOperandNumber());
1035 containerLoop.getRegionIterArgs()) {
1036 OpOperand *bbArg = containerLoop.getTiedLoopInit(containerIterArg);
1037 Value consumerOperand =
1041 initOperandPtr.set(containerIterArg);
1047 tileableProducer = dyn_cast<TilingInterface>(
clone);
1052 cast<OpResult>(sliceOpToTile.getSource()).getResultNumber();
1053 LDBG() <<
"resultNumber: " << resultNumber;
1058 FailureOr<TilingResult> tileAndFuseResult =
1059 tileableProducer.generateResultTileValue(rewriter, resultNumber, offsets,
1060 sizes, innerTileAlignments);
1062 if (failed(tileAndFuseResult)) {
1063 diag.attachNote(tileableProducer->getLoc())
1064 <<
"failed to tile producer op: " << *tileableProducer;
1069 for (
auto *tiledOp : tileAndFuseResult->tiledOps) {
1070 LDBG() <<
"tiledProducer: " << *tiledOp;
1075 auto maybeRankReduced = tensor::ExtractSliceOp::rankReduceIfNeeded(
1076 rewriter, sliceOpToTile->getLoc(), tileAndFuseResult->tiledValues[0],
1077 cast<RankedTensorType>(sliceOpToTile->getResult(0).getType()).getShape());
1078 if (failed(maybeRankReduced)) {
1080 <<
"shape types don't match (missing canonicalization?):\nTiledOp: "
1081 << tileAndFuseResult->tiledValues[0]
1082 <<
"\nSliceOp: " << sliceOpToTile.getOperation() <<
'\n';
1085 rewriter.
replaceOp(sliceOpToTile, *maybeRankReduced);
1089 rewriter,
diag, producerOp, containingOp, *tileAndFuseResult,
1090 resultNumber, offsets, sizes);
1093 if (isa<LoopLikeOpInterface>(containingOp))
1094 rewriter.
eraseOp(tileableProducer);
1096 return std::make_tuple(tileAndFuseResult->tiledOps, newContainingOp);
1109 LDBG() <<
"Try to fuse an extract use through block argument";
1111 auto tileableProducer = dyn_cast<TilingInterface>(producerOp);
1112 if (!tileableProducer) {
1114 <<
"producer is not a TileableInterface: " << *producerOp;
1119 scf::ForallOp forallOp;
1120 auto itProducerUses =
1121 llvm::find_if(tileableProducer->getUses(), [&](
OpOperand &use) {
1122 forallOp = dyn_cast<scf::ForallOp>(use.getOwner());
1126 if (!forallOp || forallOp != containingOp) {
1127 diag.attachNote(tileableProducer->getLoc())
1128 <<
"could not find a use by the containing op: " << *tileableProducer;
1143 auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user);
1144 return sliceOp && containingOp->isProperAncestor(sliceOp);
1148 if (itBBArgUsers == bbArg.
getUsers().end()) {
1150 <<
"could not find fusion opportunity for bbArg: " << bbArg;
1153 auto sliceOpToTile = cast<tensor::ExtractSliceOp>(*itBBArgUsers);
1161 int64_t resultNumber = cast<OpResult>(pUse->
get()).getResultNumber();
1162 LDBG() <<
"resultNumber: " << resultNumber;
1167 rewriter, tileableProducer->getLoc(), tileableProducer,
1168 destinationTensors))) {
1169 diag.attachNote(tileableProducer->getLoc())
1170 <<
"failed to get destination tensors for: " << *tileableProducer;
1175 bvm.
map(destinationTensors[resultNumber], bbArg);
1176 auto tileableProducerClone =
1177 cast<TilingInterface>(rewriter.
clone(*tileableProducer, bvm));
1178 llvm::scope_exit scopeGuard(
1179 [&]() { rewriter.
eraseOp(tileableProducerClone); });
1182 FailureOr<TilingResult> tileAndFuseResult =
1183 tileableProducerClone.generateResultTileValue(
1184 rewriter, resultNumber, sliceOpToTile.getMixedOffsets(),
1185 sliceOpToTile.getMixedSizes(), innerTileAlignments);
1186 if (failed(tileAndFuseResult)) {
1187 diag.attachNote(tileableProducer->getLoc())
1188 <<
"failed to tile producer op: " << *tileableProducer;
1193 auto maybeRankReduced = tensor::ExtractSliceOp::rankReduceIfNeeded(
1194 rewriter, sliceOpToTile->getLoc(), tileAndFuseResult->tiledValues[0],
1195 cast<RankedTensorType>(sliceOpToTile->getResult(0).getType()).getShape());
1196 assert(succeeded(maybeRankReduced) &&
"unexpected shape");
1197 rewriter.
replaceOp(sliceOpToTile, *maybeRankReduced);
1202 destinationTensors.front());
1205 return tileAndFuseResult->tiledOps;
1211 LDBG() <<
"Try to fuse an use by cloning";
1218 uses.push_back(&use);
1223 if (containingOp == use.getOwner()) {
1225 <<
"producer op use by containing op cannot be fused by cloning";
1233 diag.attachNote(producerOp->
getLoc()) <<
"no fusion opportunity by cloning";
1242 assert(!isa<tensor::ParallelInsertSliceOp>(use->
getOwner()) &&
1243 "Parallel insert slice is not a valid clone destination");
1244 unsigned resultNumber = cast<OpResult>(use->
get()).getResultNumber();
1245 LDBG() <<
"resultNumber: " << resultNumber;
1249 fusedOp = rewriter.
clone(*producerOp);
1251 use->
getOwner(), [&] { use->set(fusedOp->getOpResult(resultNumber)); });
1256bool transform::FuseIntoContainingOp::allowsRepeatedHandleOperands() {
1261LogicalResult transform::FuseIntoContainingOp::verify() {
1271 auto containingOps = state.
getPayloadOps(getContainingOp());
1272 if (!llvm::hasSingleElement(containingOps)) {
1274 <<
"requires exactly one containing_op handle (got "
1275 << llvm::range_size(containingOps) <<
")";
1277 Operation *containingOp = *containingOps.begin();
1286 if (std::empty(producerOps)) {
1288 results.
set(cast<OpResult>(getNewContainingOp()), {containingOp});
1295 auto getNextProducer = [&]() -> FailureOr<Operation *> {
1296 for (
const auto &it :
enumerate(remainingProducers)) {
1299 int64_t numUsesInContainingOp =
1301 return containingOp->isAncestor(op);
1306 if (numUsesInContainingOp > 0) {
1307 if (numUsesInContainingOp == 1)
1308 remainingProducers.erase(remainingProducers.begin() + it.index());
1315 while (!remainingProducers.empty()) {
1316 auto nextProducer = getNextProducer();
1317 if (
failed(nextProducer)) {
1319 <<
"could not find next producer to fuse into container";
1320 diag.attachNote(containingOp->
getLoc()) <<
"containing op";
1328 diag <<
"could not fuse " << *producerOp <<
" into " << *containingOp;
1336 rewriter,
diag, producerOp, containingOp, innerTileAlignments);
1337 if (!tiledOps.empty()) {
1338 LDBG() <<
"\nFused a direct extract use\n" << *containingOp;
1339 fusedOps.append(tiledOps);
1340 if (newContainingOp) {
1348 LogicalResult replacementStatus =
1351 (
void)replacementStatus;
1352 assert(succeeded(replacementStatus) &&
1353 "unable to update transform state mapping");
1354 rewriter.
eraseOp(containingOp);
1355 containingOp = newContainingOp;
1362 rewriter,
diag, producerOp, containingOp, innerTileAlignments);
1363 if (!tiledContainingOpOperand.empty()) {
1364 LDBG() <<
"\nFused an extract use through block argument\n"
1366 fusedOps.append(tiledContainingOpOperand);
1373 LDBG() <<
"\nFused an use by cloning\n" << *containingOp;
1374 fusedOps.push_back(cloned);
1380 results.
set(cast<OpResult>(getFusedOp()), fusedOps);
1381 results.
set(cast<OpResult>(getNewContainingOp()), {containingOp});
1385void transform::FuseIntoContainingOp::getEffects(
1403 if (isa<GenericOp>(
target)) {
1409 if (succeeded(generic)) {
1410 results.
push_back(generic->getOperation());
1413 return emitDefaultSilenceableFailure(
target);
1426 if (!isa<GenericOp>(
target)) {
1431 FailureOr<LinalgOp> named =
1433 if (succeeded(named)) {
1434 results.
push_back(named->getOperation());
1437 return emitDefaultSilenceableFailure(
target);
1451 if (interchangeVector.empty()) {
1456 unsigned numLoops = cast<LinalgOp>(
target.getOperation()).getNumLoops();
1457 if (interchangeVector.size() != numLoops) {
1458 return emitSilenceableError()
1459 << getIteratorInterchangeAttrName() <<
" has length ("
1460 << interchangeVector.size()
1461 <<
") different from the number of loops in the target operation ("
1472LogicalResult transform::InterchangeOp::verify() {
1474 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, permutation.size()));
1475 if (!std::is_permutation(sequence.begin(), sequence.end(),
1476 permutation.begin(), permutation.end())) {
1477 return emitOpError()
1478 <<
"expects iterator_interchange to be a permutation, found "
1479 << getIteratorInterchange();
1494 if (!isa<linalg::CopyOp>(targetOp)) {
1496 emitSilenceableError() <<
"only linalg.copy target ops are supported";
1497 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1501 auto copyOp = dyn_cast<linalg::CopyOp>(targetOp);
1502 if (!copyOp.hasPureBufferSemantics()) {
1504 emitSilenceableError()
1505 <<
"cannot transform a linalg.copy on tensors into a memref.copy";
1506 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1512 assert(inputs.size() == 1 &&
"expected linalg copy op with one input");
1513 assert(outputs.size() == 1 &&
"expected memref copy op with one output");
1514 Value input = inputs.front();
1515 Value output = outputs.front();
1520 if (!isa<ShapedType>(input.
getType())) {
1522 emitSilenceableError()
1523 <<
"cannot transform a linalg.copy which input has no shape";
1524 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1529 assert(isa<ShapedType>(output.
getType()));
1531 if (cast<ShapedType>(input.
getType()).getElementType() !=
1532 cast<ShapedType>(output.
getType()).getElementType()) {
1534 emitSilenceableError()
1535 <<
"cannot transform a linalg.copy with different source and "
1536 "destination element types ";
1537 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1557 if (!
target.hasPureTensorSemantics()) {
1559 <<
"lower_pack only supports tensor semantics. The target has "
1563 bool lowerPadLikeWithInsertSlice = getLowerPadLikeWithInsertSlice();
1564 FailureOr<LowerPackResult> res =
1568 <<
"cannot lower to pad + expand + transpose";
1571 transformResults.
push_back(res->expandShapeOp);
1572 transformResults.
push_back(res->transposeOp);
1584 if (!
target.hasPureTensorSemantics()) {
1586 emitSilenceableError() <<
"lower_unpack only supports tensor "
1587 "semantics. The target has memref operands";
1588 diag.attachNote(
target->getLoc()) <<
"target payload op";
1592 bool lowerUnpadLikeWithExtractSlice = getLowerUnpadLikeWithExtractSlice();
1593 FailureOr<LowerUnPackOpResult> res =
1597 emitSilenceableError()
1598 <<
"cannot lower to transpose + collapse + extract";
1599 diag.attachNote(
target->getLoc()) <<
"target payload op";
1602 transformResults.
push_back(res->emptyOp);
1603 transformResults.
push_back(res->transposeOp);
1604 transformResults.
push_back(res->collapseShapeOp);
1605 transformResults.
push_back(res->extractSliceOp);
1606 transformResults.
push_back(res->copyOp);
1617 result.addAttribute(MatchOp::getOpsAttrName(
result.name),
1626 result.addAttribute(MatchOp::getOpsAttrName(
result.name),
1628 result.addTypes(resultTypes);
1636 if (getOps().has_value())
1637 strs.insert_range(getOps()->getAsValueRange<StringAttr>());
1640 if (!llvm::hasSingleElement(payloadOps)) {
1645 bool incorrectNumOperandTypes =
false;
1652 if (getInterface().has_value()) {
1653 auto iface = getInterface().value();
1654 if (iface == transform::MatchInterfaceEnum::LinalgOp &&
1657 if (iface == transform::MatchInterfaceEnum::TilingInterface &&
1658 !isa<TilingInterface>(op))
1660 if (iface == transform::MatchInterfaceEnum::LoopLikeInterface &&
1661 !isa<LoopLikeOpInterface>(op))
1666 if (getOpAttrs().has_value()) {
1667 DictionaryAttr opAttrs = getOpAttrs().value();
1669 if (attr.getName() == getInterfaceAttrName() ||
1670 attr.getName() == getOpsAttrName())
1672 std::optional<Attribute> inherent = op->
getInherentAttr(attr.getName());
1678 if (actual != attr.getValue())
1683 if (getFilterResultType().has_value()) {
1684 Type t = getFilterResultType().value();
1689 if (getFilterOperandTypes().has_value()) {
1690 mlir::ArrayAttr types = getFilterOperandTypes().value();
1693 if (types.size() == 1) {
1696 dyn_cast<mlir::TypeAttr>(getFilterOperandTypes().value()[0]);
1697 Type t = cast<::mlir::Type>(typeattr.getValue());
1699 [&](
Type operandType) { return operandType == t; }))
1704 if (types.size() != operandTypes.size()) {
1705 incorrectNumOperandTypes =
true;
1709 for (
auto [attr, operandType] :
1710 llvm::zip_equal(getFilterOperandTypes().value(), operandTypes)) {
1711 auto typeattr = cast<mlir::TypeAttr>(attr);
1712 Type type = cast<::mlir::Type>(typeattr.getValue());
1714 if (type != operandType)
1725 (*payloadOps.begin())->walk(matchFun);
1726 if (incorrectNumOperandTypes)
1728 "type, then it must contain as much types as "
1729 "the number of operands in the target ops");
1730 results.
set(cast<OpResult>(getResult()), res);
1745 Type &targetType,
Type &lowSizeType,
1747 Type &splitPointType) {
1748 FunctionType funcType;
1750 if (failed(parser.
parseType<FunctionType>(funcType)))
1753 if (funcType.getNumInputs() != 1 || funcType.getNumResults() != 1) {
1754 parser.
emitError(typeLoc) <<
"expects a trailing functional type with one "
1755 "argument and one result";
1757 targetType = funcType.getInput(0);
1758 lowSizeType = highSizeType = splitPointType = funcType.getResult(0);
1766 if (isa<TransformParamTypeInterface>(getLowSize().
getType())) {
1767 if (
target.hasDynamicShape()) {
1768 auto diag = emitSilenceableError()
1769 <<
"cannot compute parametric tile sizes for dynamically "
1770 "shaped payload op";
1771 diag.attachNote(
target->getLoc()) <<
"payload op";
1776 target, getDimension(), getTargetSize(), getDivisor());
1778 return emitSilenceableError()
1779 <<
"failed to compute multi-size tiling sizes";
1783 results.
assign(llvm::map_range(
1785 spec->lowTileSize * spec->lowTripCount}),
1786 [&builder,
this](
int64_t value) {
1798 builder,
target, getDimension(), targetSize, divisor);
1800 return emitSilenceableError() <<
"could not generate tile size computation";
1807 {spec->lowTileSize, spec->lowTripCount});
1808 Operation *lowTileSize = spec->lowTileSize.getDefiningOp();
1809 Operation *highTileSize = spec->highTileSize.getDefiningOp();
1810 assert(lowTileSize && highTileSize && splitPoint &&
1811 "tile sizes are not produced by operations");
1819void transform::MultiTileSizesOp::getEffects(
1823 if (isa<TransformParamTypeInterface>(getLowSize().
getType()))
1829LogicalResult transform::MultiTileSizesOp::verify() {
1832 return emitOpError() <<
"expects all results type to be the same";
1851 Type linalgOpHType = transform::OperationType::get(
1852 builder.
getContext(), GenericOp::getOperationName());
1871 if (std::empty(targetOps)) {
1872 transformResults.
set(cast<OpResult>(getPackedOp()),
1877 auto linalgOp = dyn_cast<LinalgOp>(*targetOps.begin());
1878 if (!llvm::hasSingleElement(targetOps) || !linalgOp) {
1879 return emitSilenceableError()
1880 <<
"requires target to map to exactly 1 LinalgOp (got "
1881 << llvm::range_size(targetOps) <<
")";
1884 if (!linalgOp.hasPureTensorSemantics()) {
1885 return emitSilenceableError()
1886 <<
"structured.pack only supports tensor semantics. The target has "
1890 if (getMixedPackedSizes().size() != linalgOp.getNumLoops()) {
1891 return emitSilenceableError()
1892 <<
"requires number of packed sizes match the number of loops ("
1893 << getMixedPackedSizes().size() <<
" vs " << linalgOp.getNumLoops()
1900 state, *
this, packedSizes, getMixedPackedSizes());
1903 FailureOr<PackResult> maybeResult =
pack(rewriter, linalgOp, packedSizes);
1907 transformResults.
set(cast<OpResult>(getPackedOp()),
1908 {maybeResult->packedLinalgOp.getOperation()});
1912void transform::PackOp::getEffects(
1924LogicalResult transform::PackGreedilyOp::verify() {
1926 return emitOpError() << getMatmulInnerDimsOrderAttrName()
1927 <<
" is not a valid permutation";
1930 if (!getMatmulPaddedSizesNextMultipleOf().empty()) {
1931 for (
auto [s, nmo] :
1932 llvm::zip_equal(getMixedMatmulPackedSizes(),
1933 getMatmulPaddedSizesNextMultipleOf())) {
1936 (!maybeStaticPackedSize.has_value() || *maybeStaticPackedSize != 0)) {
1937 return emitOpError() <<
"at most one of the packed_size and the "
1938 "padded_sizes_next_multiple_of can be nonzero "
1939 "for the matmul strategy";
1951 for (
auto linalgOp :
1952 llvm::make_isa_range<LinalgOp>(state.
getPayloadOps(getTarget()))) {
1961 getMixedMatmulPackedSizes(),
1963 getMatmulPaddedSizesNextMultipleOf(),
1964 getMatmulInnerDimsOrder());
1965 if (succeeded(packResult)) {
1966 results.push_back(packResult->packedLinalgOp);
1969 results.push_back(linalgOp);
1971 transformResults.
set(cast<OpResult>(getPackedOp()), results);
1977 return getMixedValues(getStaticMatmulPackedSizes(), getMatmulPackedSizes(),
1981void transform::PackGreedilyOp::getEffects(
1993LogicalResult transform::PackTransposeOp::verify() {
1995 return emitOpError() << getInnerPermAttrName()
1996 <<
" is not a valid permutation";
1999 return emitOpError() << getOuterPermAttrName()
2000 <<
" is not a valid permutation";
2002 if (getInnerPerm().empty() && getOuterPerm().empty()) {
2003 return emitOpError() <<
" at least one of " << getInnerPermAttrName()
2004 <<
" or " << getOuterPermAttrName()
2005 <<
" must be specified";
2011enum class OuterOrInnerPerm { Outer = 0, Inner = 1 };
2021template <
typename RelayoutOpTy>
2022static bool isValidPackingPermutation(
2024 OuterOrInnerPerm outerOrInnerPerm = OuterOrInnerPerm::Outer) {
2026 llvm::is_one_of<RelayoutOpTy, linalg::PackOp, linalg::UnPackOp>::value,
2027 "applies to only pack or unpack operations");
2028 if (!op || permutation.empty())
2030 size_t innerRank = op.getInnerDimsPos().size();
2031 if (outerOrInnerPerm == OuterOrInnerPerm::Inner)
2035 if (std::is_same<RelayoutOpTy, linalg::PackOp>::value) {
2036 return permutation.size() == op.getSourceRank() &&
2039 return permutation.size() == op.getDestRank() &&
2047 auto packOrUnpackOps = state.
getPayloadOps(getTargetPackOrUnPackOp());
2050 if (std::empty(packOrUnpackOps)) {
2051 transformResults.
set(cast<OpResult>(getPackedOp()), {});
2052 transformResults.
set(cast<OpResult>(getPackOp()), {});
2053 transformResults.
set(cast<OpResult>(getUnPackOp()), {});
2059 if (!llvm::hasSingleElement(packOrUnpackOps) ||
2060 !llvm::hasSingleElement(linalgOps)) {
2061 return emitSilenceableError()
2062 <<
"requires target to map to exactly 1 "
2063 "packing op and 1 packed op ("
2064 <<
"got " << llvm::range_size(packOrUnpackOps) <<
" and "
2065 << llvm::range_size(linalgOps) <<
")";
2069 auto packOp = dyn_cast<linalg::PackOp>(*packOrUnpackOps.begin());
2070 auto unPackOp = dyn_cast<linalg::UnPackOp>(*packOrUnpackOps.begin());
2071 if ((!packOp && !unPackOp)) {
2072 return emitSilenceableError() <<
"requires target to map to a "
2073 "linalg.pack or linalg.unpack";
2075 LinalgOp linalgOpTarget = dyn_cast<LinalgOp>(*linalgOps.begin());
2076 if (!linalgOpTarget)
2077 return emitSilenceableError() <<
"requires a LinalgOp target";
2081 if (packOp && packOp.getResult().hasOneUse())
2082 linalgOp = dyn_cast<LinalgOp>(*(packOp.getResult().getUsers().begin()));
2084 linalgOp = unPackOp.getSource().getDefiningOp<LinalgOp>();
2085 if (linalgOp != linalgOpTarget) {
2087 packOp ? StringLiteral{
"not a single use by the LinalgOp target"}
2088 : StringLiteral{
"not produced by the LinalgOp target"};
2089 return emitSilenceableError() << errorMsg;
2095 assert(!packOp &&
"packOp must be null on entry when unPackOp is not null");
2096 OpOperand *packUse = linalgOp.getDpsInitOperand(
2097 cast<OpResult>(unPackOp.getSource()).getResultNumber());
2099 if (!packOp || !packOp.getResult().hasOneUse())
2100 return emitSilenceableError() <<
"could not find matching pack op";
2104 for (
auto permType : {OuterOrInnerPerm::Outer, OuterOrInnerPerm::Inner}) {
2106 (permType == OuterOrInnerPerm::Outer) ? getOuterPerm() : getInnerPerm();
2107 auto errorMsg = (permType == OuterOrInnerPerm::Outer)
2108 ? StringLiteral{
"invalid outer_perm"}
2109 : StringLiteral{
"invalid inner_perm"};
2110 if (!isValidPackingPermutation(packOp, perm, permType) ||
2111 !isValidPackingPermutation(unPackOp, perm, permType)) {
2113 unPackOp ? unPackOp.getOperation() : packOp.getOperation();
2114 return emitSilenceableError() << errorMsg <<
": " << *packOrUnpackOp;
2120 assert(packOp && linalgOp &&
"unexpected null op");
2124 rewriter, packOp, linalgOp, unPackOp, getOuterPerm(), getInnerPerm());
2126 assert(succeeded(res) &&
"unexpected packTranspose failure");
2129 transformResults.
set(cast<OpResult>(getPackOp()), {res->transposedPackOp});
2130 transformResults.
set(cast<OpResult>(getPackedOp()),
2131 {res->transposedLinalgOp});
2133 transformResults.
set(cast<OpResult>(getUnPackOp()),
2134 {res->transposedUnPackOp});
2136 transformResults.
set(cast<OpResult>(getUnPackOp()), {});
2151 StringRef copyBackOp,
2152 bool usePrescribedTensorShapes) {
2153 auto resultType = transform::AnyOpType::get(
b.getContext());
2159 b.getI64ArrayAttr(paddingDimensions),
2162 (padToMultipleOf.empty()
2164 :
b.getDenseI64ArrayAttr(padToMultipleOf)),
2165 b.getI64ArrayAttr(nofoldFlags),
2166 b.getArrayAttr(transposePaddings),
2167 b.getStringAttr(copyBackOp),
2169 usePrescribedTensorShapes ?
b.getUnitAttr() :
nullptr);
2177 StringRef copyBackOp,
2178 bool usePrescribedTensorShapes) {
2179 auto resultType = transform::AnyOpType::get(
b.getContext());
2183 staticPadToMultipleOf);
2189 b.getI64ArrayAttr(paddingDimensions),
2190 dynamicPadToMultipleOf,
2191 staticPadToMultipleOf,
2192 b.getI64ArrayAttr(nofoldFlags),
2193 b.getArrayAttr(transposePaddings),
2195 usePrescribedTensorShapes);
2198void PadOp::getEffects(
2206SmallVector<OpFoldResult> PadOp::getMixedPadToMultipleOf() {
2208 return getMixedValues(getStaticPadToMultipleOf(), getPadToMultipleOf(),
b);
2211DiagnosedSilenceableFailure
2212transform::PadOp::apply(transform::TransformRewriter &rewriter,
2213 transform::TransformResults &results,
2214 transform::TransformState &state) {
2215 auto transformOp = cast<TransformOpInterface>(getOperation());
2216 SmallVector<Operation *> paddedOps, padOps, copyBackOps;
2219 auto linalgTarget = dyn_cast<LinalgOp>(
target);
2220 if (!linalgTarget) {
2221 auto diag = emitSilenceableError() <<
"expected LinalgOp target";
2227 SmallVector<bool> nofoldFlags;
2228 for (int64_t packPadding :
2230 nofoldFlags.push_back(
static_cast<bool>(packPadding));
2233 SmallVector<Attribute> paddingValues;
2234 for (
auto const &[untypedAttr, elementOrTensorType] :
2235 llvm::zip(getPaddingValues(), linalgTarget->getOperandTypes())) {
2238 paddingValues.push_back(untypedAttr);
2241 auto attr = dyn_cast<TypedAttr>(untypedAttr);
2243 emitOpError(
"expects padding values to be typed attributes or poison");
2248 if (
auto stringAttr = dyn_cast<StringAttr>(attr)) {
2252 if (!parsedAttr || parsedAttr.getType() != elementType) {
2253 auto diag = this->emitOpError(
"expects a padding that parses to ")
2254 << elementType <<
", got " << untypedAttr;
2255 diag.
attachNote(linalgTarget.getLoc()) <<
"when applied to this op";
2258 paddingValues.push_back(parsedAttr);
2262 if (attr.getType() != elementType) {
2263 auto diag = this->emitOpError(
"expects a padding value of type ")
2264 << elementType <<
", got " << attr;
2265 diag.
attachNote(linalgTarget.getLoc()) <<
"when applied to this op";
2268 paddingValues.push_back(attr);
2272 SmallVector<SmallVector<int64_t>> transposePaddings;
2273 for (Attribute transposeVector : cast<ArrayAttr>(getTransposePaddings()))
2275 cast<ArrayAttr>(transposeVector)));
2282 SmallVector<int64_t> padToMultipleOf;
2284 state, transformOp, getMixedPadToMultipleOf(), padToMultipleOf);
2287 if (padToMultipleOf.empty())
2289 SmallVector<int64_t>(
options.paddingDimensions.size(), 1);
2291 options.padToMultipleOf = std::move(padToMultipleOf);
2292 options.paddingValues = std::move(paddingValues);
2293 options.nofoldFlags = std::move(nofoldFlags);
2294 if (getCopyBackOp() ==
2295 bufferization::MaterializeInDestinationOp::getOperationName()) {
2296 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::
2297 BufferizationMaterializeInDestination;
2298 }
else if (getCopyBackOp() == linalg::CopyOp::getOperationName()) {
2299 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::LinalgCopy;
2300 }
else if (getCopyBackOp() == kCopyOpNone) {
2301 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::None;
2303 llvm_unreachable(
"unsupported copy_back op");
2306 bool irChanged =
false;
2307 if (getUsePrescribedTensorShapes() &&
2308 linalgTarget.hasPureTensorSemantics()) {
2309 OpBuilder::InsertionGuard g(rewriter);
2311 for (OpOperand &operand : linalgTarget->getOpOperands()) {
2312 for (
auto [i, dim] : llvm::enumerate(linalgTarget.getShape(&operand))) {
2313 if (ShapedType::isStatic(dim))
2315 options.setSizeToPadTo(operand.getOperandNumber(), i,
2317 operand.get().getLoc(),
2324 SmallVector<Value> replacements;
2325 SmallVector<tensor::PadOp> newPadOps;
2327 replacements, newPadOps))) {
2333 auto diag = emitSilenceableError() <<
"failed to pad op";
2343 rewriter.
replaceOp(linalgTarget, replacements);
2344 paddedOps.push_back(paddedOp);
2345 padOps.append(newPadOps.begin(), newPadOps.end());
2346 if (
options.copyBackOp != LinalgPaddingOptions::CopyBackOp::None) {
2347 for (Value v : replacements) {
2348 Operation *copyBackOp = v.getDefiningOp();
2349 if (!llvm::is_contained(copyBackOps, copyBackOp))
2350 copyBackOps.push_back(copyBackOp);
2355 results.
set(cast<OpResult>(getPadded()), paddedOps);
2356 results.
set(cast<OpResult>(getPad()), padOps);
2357 results.
set(cast<OpResult>(getCopy()), copyBackOps);
2361LogicalResult transform::PadOp::verify() {
2362 SmallVector<int64_t> nofoldFlags =
2364 if (any_of(nofoldFlags, [](int64_t packPadding) {
2365 return packPadding != 0 && packPadding != 1;
2367 return emitOpError()
2368 <<
"expects nofold_flags to contain booleans (0/1), found "
2369 << getNofoldFlags();
2372 SmallVector<int64_t> paddingDimensions =
2374 if (any_of(paddingDimensions,
2375 [](int64_t paddingDimension) {
return paddingDimension < 0; })) {
2376 return emitOpError() <<
"expects padding_dimensions to contain positive "
2378 << getPaddingDimensions();
2380 if (!getMixedPadToMultipleOf().empty()) {
2381 if (getMixedPadToMultipleOf().size() != paddingDimensions.size()) {
2382 return emitOpError() <<
"expects as many multiples as padding_dimensions";
2385 ArrayAttr transposes = getTransposePaddings();
2386 for (Attribute attr : transposes) {
2388 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2389 if (!std::is_permutation(sequence.begin(), sequence.end(),
2390 transpose.begin(), transpose.end())) {
2391 return emitOpError()
2392 <<
"expects transpose_paddings to be a permutation, found "
2396 if (getCopyBackOp() !=
2397 bufferization::MaterializeInDestinationOp::getOperationName() &&
2398 getCopyBackOp() != linalg::CopyOp::getOperationName() &&
2399 getCopyBackOp() != kCopyOpNone)
2400 return emitOpError() <<
"invalid copy_back_op";
2408void transform::PadTilingInterfaceOp::build(OpBuilder &
b,
2411 ArrayRef<int64_t> paddingSizes,
2412 bool padToMultipleOf) {
2413 auto resultType = transform::AnyOpType::get(
b.getContext());
2422 :
b.getDenseI64ArrayAttr(paddingSizes)),
2424 padToMultipleOf ?
b.getUnitAttr() :
nullptr);
2427void transform::PadTilingInterfaceOp::build(
2429 ArrayRef<OpFoldResult> mixedPaddingSizes,
bool padToMultipleOf) {
2430 auto resultType = transform::AnyOpType::get(
b.getContext());
2431 SmallVector<int64_t> staticPaddingSizes;
2432 SmallVector<Value> dynamicPaddingSizes;
2434 staticPaddingSizes);
2440 dynamicPaddingSizes,
2445void transform::PadTilingInterfaceOp::getEffects(
2446 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2453SmallVector<OpFoldResult>
2454transform::PadTilingInterfaceOp::getMixedPaddingSizes() {
2459DiagnosedSilenceableFailure
2460transform::PadTilingInterfaceOp::apply(transform::TransformRewriter &rewriter,
2461 transform::TransformResults &results,
2462 transform::TransformState &state) {
2463 SmallVector<Operation *> paddedOps, padOps;
2466 auto targetOp = dyn_cast<TilingInterface>(
target);
2468 auto diag = emitSilenceableError() <<
"expected TilingInterface target";
2476 if (!isa<IndexingMapOpInterface>(targetOp.getOperation())) {
2477 auto diag = emitSilenceableError() <<
"only IndexingMapOpInterface ops "
2484 SmallVector<Attribute> paddingValues;
2485 for (
auto const &[untypedAttr, elementOrTensorType] :
2486 llvm::zip(getPaddingValues(), targetOp->getOperandTypes())) {
2487 auto attr = dyn_cast<TypedAttr>(untypedAttr);
2491 paddingValues.push_back(untypedAttr);
2495 emitOpError(
"expects padding values to be typed attributes or poison");
2499 if (
auto stringAttr = dyn_cast<StringAttr>(attr)) {
2503 if (!parsedAttr || parsedAttr.getType() != elementType) {
2504 auto diag = this->emitOpError(
"expects a padding that parses to ")
2505 << elementType <<
", got " << attr;
2506 diag.
attachNote(targetOp.getLoc()) <<
"when applied to this op";
2509 paddingValues.push_back(parsedAttr);
2513 if (attr.getType() != elementType) {
2514 auto diag = this->emitOpError(
"expects a padding value of type ")
2515 << elementType <<
", got " << attr;
2516 diag.
attachNote(targetOp.getLoc()) <<
"when applied to this op";
2519 paddingValues.push_back(attr);
2523 PadTilingInterfaceOptions
options;
2524 options.paddingValues = std::move(paddingValues);
2525 options.setPaddingSizes(getMixedPaddingSizes())
2526 .setPadToMultipleOf(getPadToMultipleOf());
2528 OpBuilder::InsertionGuard g(rewriter);
2531 rewriter, cast<TilingInterface>(targetOp.getOperation()),
2533 if (
failed(maybePadOps)) {
2534 auto diag = emitSilenceableError() <<
"failed to pad op";
2538 const auto &[paddedOperands, paddedOp, slicedResults] = maybePadOps.value();
2541 paddedOps.push_back(paddedOp);
2542 padOps.append(paddedOperands.begin(), paddedOperands.end());
2543 rewriter.
replaceOp(targetOp.getOperation(), slicedResults);
2546 results.
set(cast<OpResult>(getPadded()), paddedOps);
2547 results.
set(cast<OpResult>(getPad()), padOps);
2551LogicalResult transform::PadTilingInterfaceOp::verify() {
return success(); }
2557DiagnosedSilenceableFailure transform::HoistPadBuildPackingLoopNestOp::apply(
2558 transform::TransformRewriter &rewriter,
2559 transform::TransformResults &transformResults,
2560 transform::TransformState &state) {
2563 if (!llvm::hasSingleElement(targetOps) || !llvm::hasSingleElement(loopOps)) {
2565 <<
"requires exactly one target and one loop handle (got "
2566 << llvm::range_size(targetOps) <<
" and "
2567 << llvm::range_size(loopOps) <<
")";
2570 auto padOp = dyn_cast_or_null<tensor::PadOp>(*targetOps.begin());
2571 auto loopOp = dyn_cast_or_null<scf::ForOp>(*loopOps.begin());
2572 if (!padOp || !loopOp)
2575 FailureOr<linalg::detail::PackingResult>
result =
2581 if (
result->clonedLoopIvs.empty()) {
2582 transformResults.
set(cast<OpResult>(getPackingLoop()),
2583 {
result->hoistedPadOp.getOperation()});
2586 auto outerPackedLoop =
2588 transformResults.
set(cast<OpResult>(getPackingLoop()),
2589 {outerPackedLoop.getOperation()});
2593LogicalResult transform::HoistPadBuildPackingLoopNestOp::verify() {
2594 ArrayRef<int64_t> transpose = getTranspose();
2595 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2596 if (!std::is_permutation(sequence.begin(), sequence.end(), transpose.begin(),
2598 return emitOpError() <<
"expects transpose to be a permutation, found "
2604void transform::HoistPadBuildPackingLoopNestOp::getEffects(
2605 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2612DiagnosedSilenceableFailure
2613transform::HoistPadOp::applyToOne(transform::TransformRewriter &rewriter,
2615 transform::ApplyToEachResultList &results,
2616 transform::TransformState &state) {
2617 tensor::PadOp hoistedPadOp;
2618 SmallVector<TransposeOp> transposeOps;
2619 FailureOr<Value>
result =
2621 hoistedPadOp, transposeOps);
2632 return emitDefaultSilenceableFailure(
target);
2635LogicalResult transform::HoistPadOp::verify() {
2636 ArrayRef<int64_t> transpose = getTranspose();
2637 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2638 if (!std::is_permutation(sequence.begin(), sequence.end(), transpose.begin(),
2640 return emitOpError() <<
"expects transpose to be a permutation, found "
2650DiagnosedSilenceableFailure
2651transform::PromoteOp::applyToOne(transform::TransformRewriter &rewriter,
2653 transform::ApplyToEachResultList &results,
2654 transform::TransformState &state) {
2655 LinalgPromotionOptions promotionOptions;
2656 if (!getOperandsToPromote().empty())
2659 if (getUseFullTilesByDefault())
2661 getUseFullTilesByDefault());
2662 if (getUseOriginalSubviewSize())
2666 promotionOptions = promotionOptions.
setUseAlloca(getUseAlloca());
2667 if (!getUseFullTileBuffers().empty())
2669 llvm::to_vector(getUseFullTileBuffers().getAsValueRange<BoolAttr>()));
2670 if (getAlignment().has_value())
2671 promotionOptions = promotionOptions.
setAlignment(*getAlignment());
2672 if (getMemorySpace().has_value())
2673 promotionOptions = promotionOptions.
setMemorySpace(*getMemorySpace());
2675 if (getMapping().has_value()) {
2677 auto mapping = *getMapping();
2678 if (mapping.size() > 1)
2679 return emitDefaultDefiniteFailure(
target);
2681 auto addressSpace = cast<mlir::gpu::GPUMemorySpaceMappingAttr>(mapping[0]);
2683 if (addressSpace.getAddressSpace() ==
2684 mlir::gpu::GPUDialect::getWorkgroupAddressSpace()) {
2691 }
else if (addressSpace.getAddressSpace() ==
2692 mlir::gpu::GPUDialect::getPrivateAddressSpace()) {
2700 return emitDefaultDefiniteFailure(
target);
2705 return emitDefaultDefiniteFailure(
target);
2710 return emitDefaultDefiniteFailure(
target);
2719DiagnosedSilenceableFailure
2720transform::ReplaceOp::apply(transform::TransformRewriter &rewriter,
2721 TransformResults &transformResults,
2722 TransformState &state) {
2726 for (Operation *
target : payload) {
2727 if (
target->getNumOperands() > 0)
2729 if (!
target->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
2730 target->getNumRegions() > 0)
2732 <<
"expected target that is isolated from above";
2736 Operation *pattern = &getBodyRegion().front().front();
2737 SmallVector<Operation *> replacements;
2738 for (Operation *
target : payload) {
2739 if (getOperation()->isAncestor(
target))
2746 transformResults.
set(cast<OpResult>(getReplacement()), replacements);
2750void transform::ReplaceOp::getEffects(
2751 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2757LogicalResult transform::ReplaceOp::verify() {
2758 if (!getBodyRegion().hasOneBlock())
2759 return emitOpError() <<
"expected one block";
2760 if (std::distance(getBodyRegion().front().begin(),
2761 getBodyRegion().front().end()) != 1)
2762 return emitOpError() <<
"expected one operation in block";
2763 Operation *
replacement = &getBodyRegion().front().front();
2766 <<
"expected replacement without operands";
2767 if (!
replacement->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
2770 <<
"expect op that is isolated from above";
2778DiagnosedSilenceableFailure
2779transform::ScalarizeOp::applyToOne(transform::TransformRewriter &rewriter,
2781 transform::ApplyToEachResultList &results,
2782 transform::TransformState &state) {
2783 scf::SCFTilingOptions tilingOptions;
2784 tilingOptions.setTileSizeComputationFunction([&](OpBuilder &
b, Operation *) {
2785 SmallVector<OpFoldResult> tileSizes;
2786 Location loc =
target.getLoc();
2787 SmallVector<OpFoldResult> allShapeSizes =
2788 target.createFlatListOfOperandDims(
b, loc);
2789 AffineMap map =
target.getShapesToLoopsMap();
2792 SmallVector<OpFoldResult> shapeSizes =
2797 for (OpFoldResult shapeSize : shapeSizes) {
2799 :
b.getIndexAttr(1));
2804 FailureOr<scf::SCFTilingResult> maybeTilingResult =
tileUsingSCF(
2805 rewriter, cast<TilingInterface>(
target.getOperation()), tilingOptions);
2806 if (
failed(maybeTilingResult))
2807 return emitDefaultDefiniteFailure(
target);
2809 if (
target->getNumResults())
2814 results.
reserve(maybeTilingResult->tiledOps.size());
2815 for (Operation *tiled : maybeTilingResult->tiledOps)
2824DiagnosedSilenceableFailure
2825transform::ConvertToLoopsOp::apply(transform::TransformRewriter &rewriter,
2826 transform::TransformResults &results,
2827 transform::TransformState &state) {
2828 SmallVector<Operation *> loops;
2830 auto tilingOp = dyn_cast<TilingInterface>(*
target);
2832 DiagnosedSilenceableFailure diag =
2833 emitSilenceableError()
2834 <<
"expected the payload to implement TilingInterface";
2839 FailureOr<SmallVector<scf::ForOp>> generatedLoops =
2840 scf::lowerToLoopsUsingSCFForOp(rewriter, tilingOp);
2841 if (
failed(generatedLoops))
2842 return emitDefaultDefiniteFailure(
target);
2843 for (scf::ForOp &loop : *generatedLoops) {
2844 loops.push_back(loop.getOperation());
2848 results.
set(cast<OpResult>(getResult()), loops);
2856DiagnosedSilenceableFailure
2857transform::RewriteInDestinationPassingStyleOp::applyToOne(
2858 transform::TransformRewriter &rewriter, Operation *
target,
2859 transform::ApplyToEachResultList &results,
2860 transform::TransformState &state) {
2862 FailureOr<Operation *> maybeResult =
2864 .Case<DestinationStyleOpInterface>([](
auto op) {
return op; })
2865 .Case<tensor::FromElementsOp, tensor::GenerateOp, tensor::PadOp>(
2866 [&rewriter](
auto op) {
2870 return emitDefaultSilenceableFailure(
target);
2879DiagnosedSilenceableFailure
2880SplitOp::apply(transform::TransformRewriter &rewriter,
2881 TransformResults &results, TransformState &state) {
2883 SmallVector<Operation *> payload =
2886 bool isMultiwaySplit = getMultiway();
2888 if (isMultiwaySplit && !llvm::hasSingleElement(payload)) {
2890 <<
"requires exactly one target when "
2891 "multiway split is enabled (got "
2892 << llvm::range_size(payload) <<
")";
2895 SmallVector<OpFoldResult> chunkSizes;
2897 if (!isMultiwaySplit)
2898 chunkSizes.reserve(payload.size());
2900 if (getDynamicChunkSizes()) {
2902 if (isa<TransformHandleTypeInterface>(getDynamicChunkSizes().
getType())) {
2903 chunkSizes = llvm::map_to_vector(
2904 state.
getPayloadOps(getDynamicChunkSizes()), [&](Operation *op) {
2907 diag = emitSilenceableError()
2908 <<
"expected dynamic split point handle to point to a "
2909 "single-result index-typed op";
2915 chunkSizes = llvm::map_to_vector(
2916 state.
getParams(getDynamicChunkSizes()),
2917 [](Attribute attr) {
return OpFoldResult(attr); });
2924 if (!isMultiwaySplit && chunkSizes.size() != payload.size()) {
2926 <<
"expected the dynamic split point handle to point to as "
2928 << chunkSizes.size() <<
") as the target handle ("
2929 << payload.size() <<
")";
2932 chunkSizes.resize(payload.size(),
2936 auto checkStructuredOpAndDimensions =
2937 [&](LinalgOp linalgOp, Location loc) -> DiagnosedSilenceableFailure {
2939 auto diag = emitSilenceableError() <<
"only applies to structured ops";
2944 if (getDimension() >= linalgOp.getNumLoops()) {
2945 auto diag = emitSilenceableError() <<
"dimension " << getDimension()
2946 <<
" does not exist in target op";
2953 auto checkFailureInSplitting =
2954 [&](
bool hasFailed, Location loc) -> DiagnosedSilenceableFailure {
2963 SmallVector<Operation *> opList;
2964 if (isMultiwaySplit) {
2967 TilingInterface head, tail;
2968 Operation *
target = payload.front();
2970 LinalgOp linalgOp = dyn_cast<LinalgOp>(
target);
2973 DiagnosedSilenceableFailure diag =
2974 checkStructuredOpAndDimensions(linalgOp,
target->getLoc());
2978 for (
auto &&[idx, chunkSize] : llvm::enumerate(chunkSizes)) {
2981 target = tail.getOperation();
2986 linalgOp = cast<LinalgOp>(
target);
2987 Location loc =
target->getLoc();
2991 rewriter, cast<TilingInterface>(linalgOp.getOperation()),
2992 getDimension(), chunkSize);
2995 DiagnosedSilenceableFailure diag =
2996 checkFailureInSplitting(!head && !tail, loc);
3000 opList.push_back(head.getOperation());
3005 opList.push_back(tail.getOperation());
3009 SmallVector<Operation *> first, second;
3010 Operation *noSecondPart =
nullptr;
3011 for (
const auto &pair : llvm::zip(payload, chunkSizes)) {
3012 Operation *
target = std::get<0>(pair);
3013 Location loc =
target->getLoc();
3014 LinalgOp linalgOp = dyn_cast<LinalgOp>(
target);
3015 DiagnosedSilenceableFailure diag =
3016 checkStructuredOpAndDimensions(linalgOp,
target->getLoc());
3022 std::tie(first.emplace_back(), second.emplace_back()) =
linalg::splitOp(
3023 rewriter, cast<TilingInterface>(linalgOp.getOperation()),
3024 getDimension(), std::get<1>(pair));
3027 DiagnosedSilenceableFailure diagSplit =
3028 checkFailureInSplitting(!first.back() && !second.back(), loc);
3033 if (!second.back()) {
3039 if (second.size() != first.size() && !second.empty()) {
3040 auto diag = emitSilenceableError()
3041 <<
"splitting does not produce the second part for a subset "
3044 <<
"expected splitting to produce the second part of all "
3045 "or none of the targets";
3047 <<
"first target with no second part";
3051 opList.append(first);
3052 if (!second.empty())
3053 opList.append(second);
3055 results.
set(cast<OpResult>(getSplitList()), opList);
3059void SplitOp::getEffects(
3060 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3062 if (getDynamicChunkSizes())
3068ParseResult SplitOp::parse(OpAsmParser &parser, OperationState &
result) {
3069 OpAsmParser::UnresolvedOperand
target, dynamicChunkSizes;
3070 IntegerAttr staticChunkSizes;
3074 OptionalParseResult dynamicPointParseResult =
3076 if (!dynamicPointParseResult.
has_value()) {
3077 int64_t staticChunkSizesValue;
3091 if (dynamicPointParseResult.
has_value()) {
3092 Type chunkSizesType;
3105 SplitOp::getStaticChunkSizesAttrName(
result.name).getValue(),
3107 result.addTypes(targetType);
3111void SplitOp::print(OpAsmPrinter &printer) {
3112 printer <<
" " << getTarget() <<
" after ";
3113 int64_t staticChunkSize =
static_cast<int64_t
>(getStaticChunkSizes());
3114 if (staticChunkSize != ShapedType::kDynamic)
3115 printer << staticChunkSize;
3117 printer << getDynamicChunkSizes();
3119 NamedAttrList attrs(getOperation()->getDiscardableAttrDictionary());
3120 attrs.append(getDimensionAttrName(), getDimensionAttr());
3121 if (UnitAttr multiway = getMultiwayAttr())
3122 attrs.append(getMultiwayAttrName(), multiway);
3124 printer <<
" : " << getTarget().getType();
3125 if (staticChunkSize == ShapedType::kDynamic)
3126 printer <<
", " << getDynamicChunkSizes().getType();
3129LogicalResult SplitOp::verify() {
3130 if ((
static_cast<int64_t
>(getStaticChunkSizes()) != ShapedType::kDynamic) ^
3131 (getDynamicChunkSizes() ==
nullptr)) {
3132 return emitOpError() <<
"expects either a dynamic or a static split "
3133 "point to be provided";
3142void transform::SplitReductionOp::build(
3143 OpBuilder &builder, OperationState &
result, Value
target,
3144 int64_t splitFactor, int64_t insertSplitDimension,
bool innerParallel,
3145 bool useScalingAlgorithm,
bool useAlloc) {
3148 result.addAttribute(SplitReductionOp::getSplitFactorAttrName(
result.name),
3151 SplitReductionOp::getInsertSplitDimensionAttrName(
result.name),
3153 if (innerParallel) {
3154 result.addAttribute(SplitReductionOp::getInnerParallelAttrName(
result.name),
3157 if (useScalingAlgorithm) {
3159 SplitReductionOp::getUseScalingAlgorithmAttrName(
result.name),
3163 result.addAttribute(SplitReductionOp::getUseAllocAttrName(
result.name),
3166 auto resultType = transform::AnyOpType::get(ctx);
3167 result.addTypes({resultType, resultType, resultType, resultType});
3170DiagnosedSilenceableFailure transform::SplitReductionOp::applyToOne(
3171 transform::TransformRewriter &rewriter, LinalgOp
target,
3172 transform::ApplyToEachResultList &results,
3173 transform::TransformState &state) {
3175 return linalg::SplitReductionOptions{int64_t(getSplitFactor()),
3176 unsigned(getInsertSplitDimension()),
3177 bool(getInnerParallel())};
3180 FailureOr<SplitReductionResult> splitResult =
3181 (getUseScalingAlgorithm())
3185 return emitDefaultDefiniteFailure(
target);
3187 results.
push_back(splitResult->initOrAlloc);
3189 results.
push_back(splitResult->splitLinalgOp);
3190 results.
push_back(splitResult->resultCombiningLinalgOp);
3198void transform::TileReductionUsingForOp::build(
3199 OpBuilder &builder, OperationState &
result, Value
target,
3200 ArrayRef<int64_t> staticTileSizes) {
3207 auto opTy = transform::AnyOpType::get(ctx);
3213 staticTileSizesAttr);
3216DiagnosedSilenceableFailure transform::TileReductionUsingForOp::applyToOne(
3217 transform::TransformRewriter &rewriter, Operation *
target,
3218 transform::ApplyToEachResultList &results,
3219 transform::TransformState &state) {
3222 auto partialReductionOp = dyn_cast<PartialReductionOpInterface>(
target);
3223 if (!partialReductionOp) {
3226 "Operation should implement PartialReductionOpInterface");
3229 SmallVector<unsigned> reductionDims =
3231 if (reductionDims.empty()) {
3232 for (
auto [idx, iteratorType] :
3233 llvm::enumerate(partialReductionOp.getLoopIteratorTypes())) {
3234 if (iteratorType == utils::IteratorType::reduction)
3235 reductionDims.push_back(idx);
3239 scf::SCFTilingOptions
options;
3240 options.setLoopType(scf::SCFTilingOptions::LoopType::ForOp);
3241 options.setReductionTilingStrategy(
3244 options.setReductionDims(reductionDims);
3245 FailureOr<scf::SCFTilingResult>
result =
3246 scf::tileUsingSCF(rewriter, partialReductionOp,
options);
3250 "failed to tile using partial reduction");
3253 for (Value initValue :
result->initialValues)
3255 for (
auto *parallelTiledOp :
result->tiledOps)
3257 for (
auto *mergeOp :
result->mergeOps)
3267void transform::TileReductionUsingForallOp::build(
3268 OpBuilder &builder, OperationState &
result, Value
target,
3269 ArrayRef<int64_t> staticNumThreads, ArrayRef<int64_t> staticTileSizes,
3277 auto opTy = transform::AnyOpType::get(ctx);
3284 staticNumThreadsAttr,
3285 staticTileSizesAttr,
3289DiagnosedSilenceableFailure transform::TileReductionUsingForallOp::applyToOne(
3290 transform::TransformRewriter &rewriter, Operation *
target,
3291 transform::ApplyToEachResultList &results,
3292 transform::TransformState &state) {
3295 auto partialReductionOp = dyn_cast<PartialReductionOpInterface>(
target);
3296 if (!partialReductionOp) {
3299 "Operation should implement PartialReductionOpInterface");
3301 SmallVector<OpFoldResult> numThreads =
3303 SmallVector<OpFoldResult> tileSizes =
3306 scf::SCFTilingOptions
options;
3307 options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
3308 options.setReductionTilingStrategy(
3310 if (!getNumThreads().empty()) {
3311 options.setNumThreads(numThreads);
3313 options.setTileSizes(tileSizes);
3315 if (
auto mapping = getMapping()) {
3316 options.setMapping(mapping.value().getValue());
3318 SmallVector<unsigned> reductionDims =
3320 if (reductionDims.empty()) {
3321 for (
auto [idx, iteratorType] :
3322 llvm::enumerate(partialReductionOp.getLoopIteratorTypes())) {
3323 if (iteratorType == utils::IteratorType::reduction)
3324 reductionDims.push_back(idx);
3327 options.setReductionDims(reductionDims);
3328 FailureOr<scf::SCFTilingResult>
result =
3329 scf::tileUsingSCF(rewriter, partialReductionOp,
options);
3332 auto diag = emitSilenceableError() <<
"could not tile reduction";
3337 for (Value initValue :
result->initialValues)
3339 for (
auto *parallelTiledOp :
result->tiledOps)
3341 for (
auto *mergeOp :
result->mergeOps)
3351DiagnosedSilenceableFailure
3352transform::ContinuousTileSizesOp::apply(transform::TransformRewriter &rewriter,
3353 TransformResults &transformResults,
3354 TransformState &state) {
3356 SmallVector<Operation *> targetOps =
3359 if (!llvm::hasSingleElement(targetOps)) {
3361 <<
"requires exactly one target (got " << llvm::range_size(targetOps)
3365 Operation *
target = *targetOps.begin();
3366 auto linalgOp = dyn_cast<LinalgOp>(
target);
3367 auto tileableOp = dyn_cast<TilingInterface>(
target);
3372 OpBuilder builder(linalgOp.getContext());
3374 if (isa<TransformParamTypeInterface>(getChunkSizes().
getType())) {
3375 if (linalgOp.hasDynamicShape()) {
3376 auto diag = emitSilenceableError()
3377 <<
"cannot compute parametric tile sizes for dynamically "
3378 "shaped payload op";
3379 diag.
attachNote(linalgOp->getLoc()) <<
"payload op";
3383 FailureOr<StaticContinuousTileSizeSpecification> spec =
3387 return emitSilenceableError()
3388 <<
"failed to compute multi-size tiling sizes";
3391 SmallVector<int64_t> chunkSizes;
3393 for (
auto &&[tileSize, tripCount] :
3394 llvm::zip_equal(spec->tileSizes, spec->tripCounts))
3395 chunkSizes.push_back(tileSize * tripCount);
3397 auto getI64AttrsFromI64 = [&](ArrayRef<int64_t> values) {
3398 return llvm::map_to_vector(values, [&](int64_t value) -> Attribute {
3403 getI64AttrsFromI64(spec->tileSizes));
3404 transformResults.
setParams(cast<OpResult>(getChunkSizes()),
3405 getI64AttrsFromI64(chunkSizes));
3412 OpFoldResult targetSize = builder.
getIndexAttr(getTargetSize());
3413 unsigned dimension = getDimension();
3416 builder, tileableOp, dimension, targetSize,
true);
3418 return emitSilenceableError() <<
"could not generate tile size computation";
3423 auto apply = [&](AffineExpr expr, ArrayRef<OpFoldResult> ofrs) -> Value {
3428 SmallVector<Value> chunkSizes;
3430 for (
auto &&[tileSize, tripCount] :
3431 llvm::zip_equal(spec->tileSizes, spec->tripCounts)) {
3432 splitPoint = apply(s0 * s1, {tileSize, tripCount});
3433 chunkSizes.push_back(splitPoint);
3436 auto getDefiningOps = [&](ArrayRef<Value> values) {
3437 return llvm::map_to_vector(values, [&](Value value) -> Operation * {
3443 getDefiningOps(spec->tileSizes));
3444 transformResults.
set(cast<OpResult>(getChunkSizes()),
3445 getDefiningOps(chunkSizes));
3450LogicalResult transform::ContinuousTileSizesOp::verify() {
3453 return emitOpError() <<
"expects all results type to be the same";
3459void transform::ContinuousTileSizesOp::getEffects(
3460 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3477 Type &tileSizesType,
3478 Type &chunkSizesType) {
3479 FunctionType funcType;
3481 if (failed(parser.
parseType<FunctionType>(funcType)))
3484 if (funcType.getNumInputs() != 1 || funcType.getNumResults() != 1) {
3485 parser.
emitError(typeLoc) <<
"expects a trailing functional type with one "
3486 "argument and one result";
3488 targetType = funcType.getInput(0);
3489 tileSizesType = chunkSizesType = funcType.getResult(0);
3498void transform::TileUsingForOp::build(
3500 Value
target, ArrayRef<int64_t> staticTileSizes,
3501 ArrayRef<int64_t> interchange,
3502 std::optional<ArrayRef<bool>> scalableSizes) {
3503 return build(builder,
result, loopTypes,
3507 interchange, scalableSizes);
3510void transform::TileUsingForOp::build(
3511 OpBuilder &builder, OperationState &
result, Value
target,
3512 ArrayRef<int64_t> staticTileSizes, ArrayRef<int64_t> interchange,
3513 std::optional<ArrayRef<bool>> scalableSizes) {
3516 interchange, scalableSizes);
3519void transform::TileUsingForOp::build(
3520 OpBuilder &builder, OperationState &
result, Value
target,
3521 ArrayRef<OpFoldResult> mixedTileSizes, ArrayRef<int64_t> interchange,
3522 std::optional<ArrayRef<bool>> scalableSizes) {
3525 SmallVector<Type> loopTypes(1, builder.
getType<transform::AnyOpType>());
3526 build(builder,
result, loopTypes,
target, mixedTileSizes, interchange,
3530void transform::TileUsingForOp::build(
3531 OpBuilder &builder, OperationState &
result, Value
target,
3532 ArrayRef<OpFoldResult> mixedTileSizes,
3533 ArrayRef<OpFoldResult> mixedInterchange,
3534 std::optional<ArrayRef<bool>> scalableSizes) {
3537 SmallVector<Type> loopTypes(1, builder.
getType<transform::AnyOpType>());
3538 build(builder,
result, loopTypes,
target, mixedTileSizes, mixedInterchange,
3542void transform::TileUsingForOp::build(
3544 Value
target, ArrayRef<OpFoldResult> mixedTileSizes,
3545 ArrayRef<int64_t> interchange,
3546 std::optional<ArrayRef<bool>> scalableSizes) {
3547 SmallVector<OpFoldResult> mixedInterchange =
3549 build(builder,
result, loopTypes,
target, mixedTileSizes, mixedInterchange,
3553void transform::TileUsingForOp::build(
3555 Value
target, ArrayRef<OpFoldResult> mixedTileSizes,
3556 ArrayRef<OpFoldResult> mixedInterchange,
3557 std::optional<ArrayRef<bool>> scalableSizes) {
3558 SmallVector<int64_t> staticTileSizes;
3559 SmallVector<Value> dynamicTileSizes;
3560 SmallVector<int64_t> staticInterchange;
3561 SmallVector<Value> dynamicInterchange;
3570 unsigned numExpectedLoops =
3571 staticTileSizes.size() - llvm::count(staticTileSizes, 0);
3572 SmallVector<Type> resultTypes;
3573 resultTypes.reserve(numExpectedLoops);
3574 assert((loopTypes.size() == 1 || loopTypes.size() == numExpectedLoops) &&
3575 "expected one loop type or as many as loops");
3576 if (loopTypes.size() == 1)
3577 resultTypes.append(numExpectedLoops, loopTypes[0]);
3579 llvm::append_range(resultTypes, loopTypes);
3580 SmallVector<bool> expandedScalableSizes(mixedTileSizes.size(),
false);
3581 if (scalableSizes.has_value())
3582 expandedScalableSizes.assign(scalableSizes->begin(), scalableSizes->end());
3583 Value packedTileSizes;
3591 staticTileSizesAttr,
3592 staticInterchangeAttr,
3593 expandedScalableSizes);
3596LogicalResult transform::TileUsingForOp::verify() {
3597 bool hasPackedTiles = getPackedTileSizes() != Value();
3598 bool hasPackedInterchange = getPackedInterchange() != Value();
3601 "tile_sizes and packed_tile_sizes are mutually exclusive");
3602 if (!getMixedInterchange().empty() && hasPackedInterchange)
3604 "interchange and packed_interchange are mutually exclusive");
3605 if (hasPackedTiles && !getScalableSizes().empty())
3607 "scalable tile_sizes are not supported with packed_tile_sizes");
3610 return emitOpError(
"expected same number of sizes (")
3612 << getScalableSizes().size() <<
")";
3614 auto iterspaceRank = getStaticSizes().size();
3615 ArrayRef<int64_t> permutation = getStaticInterchange();
3616 if (permutation.size() > iterspaceRank)
3617 return emitOpError()
3618 <<
"interchange length exceeds iteration space dimensions ("
3619 << iterspaceRank <<
"), found " << getInterchange();
3620 SmallVector<bool> seen(iterspaceRank,
false);
3621 for (int64_t v : permutation) {
3622 if (!ShapedType::isDynamic(v)) {
3623 if (v < 0 || v >=
static_cast<int64_t
>(iterspaceRank))
3624 return emitOpError() <<
"expects interchange values to be in range [0, "
3625 << iterspaceRank <<
"), found: " << v;
3627 return emitOpError() <<
"found duplicate interchange value: " << v;
3632 ArrayRef<int64_t> staticSizes = getStaticSizes();
3633 unsigned numExpectedLoops =
3634 hasPackedTiles ? 1 : staticSizes.size() - llvm::count(staticSizes, 0);
3635 if (getLoops().size() != numExpectedLoops)
3636 return emitOpError(
"expected number of loops to tile (")
3637 << numExpectedLoops <<
") to match number of `loops` results ("
3638 << getLoops().size() <<
")";
3642DiagnosedSilenceableFailure
3643transform::TileUsingForOp::apply(transform::TransformRewriter &rewriter,
3644 TransformResults &transformResults,
3645 TransformState &state) {
3646 ArrayRef<int64_t> tileSizes = getStaticSizes();
3647 bool hasPackedTiles = getPackedTileSizes() != Value();
3648 bool hasPackedInterchange = getPackedInterchange() != Value();
3649 auto transformOp = cast<TransformOpInterface>(getOperation());
3651 SmallVector<OpFoldResult> mixedInterchange;
3652 if (hasPackedInterchange) {
3653 DiagnosedSilenceableFailure status =
3655 state, transformOp, mixedInterchange, getPackedInterchange());
3659 mixedInterchange = getMixedInterchange();
3661 SmallVector<int64_t> tileInterchange;
3663 state, transformOp, mixedInterchange, tileInterchange);
3667 SmallVector<Operation *> targets =
3669 SmallVector<SmallVector<Operation *>> dynamicSizeProducers;
3670 SmallVector<SmallVector<int64_t>> paramSizes;
3671 SmallVector<OpFoldResult> mixedTileSizes;
3672 if (hasPackedTiles) {
3674 state, transformOp, mixedTileSizes, getPackedTileSizes());
3682 if (isa<TransformParamTypeInterface>(transformValue.getType())) {
3683 dynamicSizeProducers.push_back({});
3684 ArrayRef<Attribute> params = state.
getParams(transformValue);
3685 paramSizes.push_back(llvm::map_to_vector(params, [](Attribute attr) {
3686 return cast<IntegerAttr>(attr).getValue().getSExtValue();
3689 if (paramSizes.back().size() != targets.size()) {
3690 DiagnosedSilenceableFailure diag =
3691 emitSilenceableError()
3692 <<
"expected as many parameter values ("
3693 << dynamicSizeProducers.back().size() <<
") as target ops ("
3694 << targets.size() <<
")";
3695 diag.
attachNote(transformValue.getLoc()) <<
"for this parameter";
3701 paramSizes.push_back({});
3702 dynamicSizeProducers.push_back(
3705 if (dynamicSizeProducers.back().size() != targets.size()) {
3706 DiagnosedSilenceableFailure diag =
3707 emitSilenceableError()
3708 <<
"expected as many dynamic size-producing operations ("
3709 << dynamicSizeProducers.back().size() <<
") as target ops ("
3710 << targets.size() <<
")";
3711 diag.
attachNote(transformValue.getLoc()) <<
"for this handle";
3715 for (Operation *op : dynamicSizeProducers.back()) {
3721 DiagnosedSilenceableFailure diag =
3722 emitSilenceableError() <<
"expected sizes to be produced by ops "
3723 "with a single index-type result";
3725 diag.
attachNote(transformValue.getLoc()) <<
"for this handle";
3731 SmallVector<Operation *> tiled;
3732 SmallVector<SmallVector<Operation *, 4>, 4> loops;
3735 ? llvm::count_if(mixedTileSizes,
3736 [](OpFoldResult ofr) {
3737 if (
auto attr = dyn_cast<Attribute>(ofr))
3738 return cast<IntegerAttr>(attr).getInt() != 0;
3741 : getLoops().size();
3742 loops.resize(numLoops);
3743 auto scalableSizes = getScalableSizes();
3744 for (
auto [i, op] : llvm::enumerate(targets)) {
3745 auto tilingInterface = dyn_cast<TilingInterface>(op);
3746 if (!tilingInterface) {
3747 DiagnosedSilenceableFailure diag =
3748 emitSilenceableError()
3749 <<
"only ops implementing TilingInterface are supported";
3753 if (isa<linalg::RelayoutOpInterface>(op) &&
3754 !cast<DestinationStyleOpInterface>(op).hasPureTensorSemantics()) {
3755 DiagnosedSilenceableFailure diag =
3756 emitSilenceableError()
3757 <<
"tiling only supports tensor semantics for linalg.pack / "
3758 "linalg.unpack. The target has memref operands";
3763 int64_t iterspaceRank = tilingInterface.getLoopIteratorTypes().size();
3764 if (tileInterchange.size() >
static_cast<size_t>(iterspaceRank)) {
3765 return emitSilenceableError()
3766 <<
"interchange length exceeds iteration space dimensions ("
3767 << iterspaceRank <<
")";
3769 SmallVector<bool> seen(iterspaceRank,
false);
3770 for (int64_t v : tileInterchange) {
3771 if (v < 0 || v >= iterspaceRank) {
3772 return emitSilenceableError()
3773 <<
"expects interchange values to be in range [0, "
3774 << iterspaceRank <<
"), found: " << v;
3777 return emitSilenceableError()
3778 <<
"found duplicate interchange value: " << v;
3783 if (tileSizes.size() > tilingInterface.getLoopIteratorTypes().size()) {
3784 DiagnosedSilenceableFailure diag =
3785 emitSilenceableError()
3786 <<
"too many tiles provided, expected at most "
3787 << tilingInterface.getLoopIteratorTypes().size() <<
" found "
3788 << tileSizes.size();
3793 scf::SCFTilingOptions tilingOptions;
3794 if (!hasPackedTiles && tileSizes.empty()) {
3795 tilingOptions.setTileSizeComputationFunction(
3796 [](OpBuilder &, Operation *) -> SmallVector<OpFoldResult> {
3799 }
else if (hasPackedTiles) {
3800 tilingOptions.setTileSizes(mixedTileSizes);
3802 tilingOptions.setTileSizeComputationFunction([&, index = i](OpBuilder &
b,
3804 SmallVector<OpFoldResult> sizes;
3805 sizes.reserve(tileSizes.size());
3806 unsigned dynamicIdx = 0;
3808 for (
auto [ofrIdx, ofr] : llvm::enumerate(
getMixedSizes())) {
3809 if (
auto attr = llvm::dyn_cast_if_present<Attribute>(ofr)) {
3810 if (scalableSizes[ofrIdx]) {
3812 b, getLoc(), cast<IntegerAttr>(attr).getInt());
3814 vector::VectorScaleOp::create(
b, getLoc(),
b.getIndexType());
3816 arith::MulIOp::create(
b, getLoc(), val, vscale).getResult());
3818 sizes.push_back(attr);
3822 ArrayRef<Operation *> dynamicSizes = dynamicSizeProducers[dynamicIdx];
3823 ArrayRef<int64_t> params = paramSizes[dynamicIdx];
3825 assert((dynamicSizes.empty() ^ params.empty()) &&
3826 "expected either dynamic sizes or parameters");
3827 if (!params.empty()) {
3828 sizes.push_back(
b.getIndexAttr(params[index]));
3830 sizes.push_back(dynamicSizes[index]->getResult(0));
3837 tilingOptions.setInterchange(tileInterchange);
3838 tilingOptions.setInnerTileAlignments(
3840 FailureOr<scf::SCFTilingResult> maybeTilingResult =
3841 tileUsingSCF(rewriter, tilingInterface, tilingOptions);
3842 if (
failed(maybeTilingResult))
3845 rewriter.
replaceOp(op, maybeTilingResult->replacements);
3847 tiled.append(maybeTilingResult->tiledOps);
3848 for (
const auto &en2 : llvm::enumerate(maybeTilingResult->loops))
3849 loops[en2.index()].push_back(en2.value());
3852 transformResults.
set(cast<OpResult>(getTiledLinalgOp()), tiled);
3853 if (hasPackedTiles) {
3855 SmallVector<Operation *> flattenedLoops;
3856 for (
auto [targetIdx, _] : llvm::enumerate(targets))
3857 for (
auto [loopIdx, __] : llvm::enumerate(loops))
3858 flattenedLoops.push_back(loops[loopIdx][targetIdx]);
3859 transformResults.
set(cast<OpResult>(getLoops().front()), flattenedLoops);
3861 for (
const auto &en : llvm::enumerate(loops))
3862 transformResults.
set(cast<OpResult>(getLoops()[en.index()]), en.value());
3868SmallVector<OpFoldResult> transform::TileUsingForOp::getMixedSizes() {
3870 ArrayRef<int64_t> tileSizes = getStaticSizes();
3871 SmallVector<OpFoldResult> results;
3872 results.reserve(tileSizes.size());
3873 unsigned dynamicPos = 0;
3875 for (int64_t size : tileSizes) {
3876 if (size == ShapedType::kDynamic) {
3877 results.push_back(dynamic[dynamicPos++]);
3885SmallVector<OpFoldResult> transform::TileUsingForOp::getMixedInterchange() {
3889void transform::TileUsingForOp::getEffects(
3890 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3904void transform::TileUsingForallOp::build(OpBuilder &builder,
3906 ArrayRef<int64_t> staticTileSizes,
3907 transform::TileSizesSpec,
3909 return build(builder,
result,
3917void transform::TileUsingForallOp::build(OpBuilder &builder,
3919 ArrayRef<OpFoldResult> mixedTileSizes,
3920 transform::TileSizesSpec,
3922 SmallVector<int64_t> staticTileSizes;
3923 SmallVector<Value> dynamicTileSizes;
3929 auto operationType = transform::AnyOpType::get(ctx);
3932 TypeRange{operationType, operationType},
3939 staticTileSizesAttr,
3943void transform::TileUsingForallOp::build(OpBuilder &builder,
3945 ArrayRef<int64_t> staticNumThreads,
3946 transform::NumThreadsSpec,
3950 NumThreadsSpec(), mapping);
3953void transform::TileUsingForallOp::build(OpBuilder &builder,
3955 ArrayRef<OpFoldResult> mixedNumThreads,
3956 transform::NumThreadsSpec,
3958 SmallVector<int64_t> staticNumThreads;
3959 SmallVector<Value> dynamicNumThreads;
3966 auto operationType = transform::AnyOpType::get(ctx);
3969 TypeRange{operationType, operationType},
3975 staticNumThreadsAttr,
3982static SmallVector<OpFoldResult>
3988 AffineExpr normalizedUbExpr = (s1 - s0).ceilDiv(s2);
3990 for (
auto [lb,
ub, step] : llvm::zip_equal(lbs, ubs, steps)) {
3992 rewriter, loc, normalizedUbExpr, {lb,
ub, step});
3993 normalizedUbs.push_back(normalizedUb);
3995 return normalizedUbs;
4011 for (
auto [iv, lb, step] : llvm::zip_equal(ivs, lbs, steps)) {
4014 denormalizedIvs.push_back(
4017 return denormalizedIvs;
4028 scf::ForallOp loop) {
4045 auto normalizedForallOp = scf::ForallOp::create(
4046 rewriter, loc, normalizedLbs, normalizedUbs, normalizedSteps,
4047 loop.getOutputs(), loop.getMapping(),
4050 auto normalizedLoopIvs = normalizedForallOp.getInductionVars();
4052 Block *normalizedLoopBlock = normalizedForallOp.getBody();
4057 argValues.append(normalizedForallOp.getRegionIterArgs().begin(),
4058 normalizedForallOp.getRegionIterArgs().end());
4059 Block *origLoopBlock = loop.getBody();
4060 rewriter.
mergeBlocks(origLoopBlock, normalizedLoopBlock, argValues);
4062 rewriter.
replaceOp(loop, normalizedForallOp);
4063 return normalizedForallOp;
4071 scf::SCFTilingResult &tilingResult) {
4073 auto tileableOp = dyn_cast<TilingInterface>(
target);
4076 transformOp.emitSilenceableError()
4077 <<
"only TilingInterface ops are supported";
4078 diag.attachNote(
target->getLoc()) <<
"target op";
4081 if (isa<linalg::RelayoutOpInterface>(
target) &&
4082 !cast<DestinationStyleOpInterface>(
target).hasPureTensorSemantics()) {
4084 transformOp.emitSilenceableError()
4085 <<
"tiling only supports tensor semantics for linalg.pack / "
4086 "linalg.unpack. The target has memref operands";
4087 diag.attachNote(
target->getLoc()) <<
"target op";
4091 scf::SCFTilingOptions
options;
4092 options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
4093 if (!mixedNumThreads.empty()) {
4094 options.setNumThreads(mixedNumThreads);
4096 options.setTileSizes(mixedTileSizes);
4099 options.setMapping(mapping.value().getValue());
4101 FailureOr<scf::SCFTilingResult> maybeTilingResult =
4102 scf::tileUsingSCF(rewriter, tileableOp,
options);
4104 if (failed(maybeTilingResult))
4105 return transformOp.emitDefaultSilenceableFailure(tileableOp);
4107 rewriter.
replaceOp(tileableOp, maybeTilingResult->replacements);
4109 tilingResult = *maybeTilingResult;
4113 if (mixedNumThreads.empty() && !tilingResult.loops.empty()) {
4114 auto generatedForallOp = cast<scf::ForallOp>(tilingResult.loops.front());
4117 scf::ForallOp normalizedForallOp =
4119 tilingResult.loops.front() = normalizedForallOp;
4129 auto transformOp = cast<TransformOpInterface>(getOperation());
4138 getPackedNumThreads()
4140 state, transformOp, mixedNumThreads, getPackedNumThreads())
4142 state, transformOp, mixedNumThreads, getMixedNumThreads());
4146 status = getPackedTileSizes()
4148 state, transformOp, mixedTileSizes, getPackedTileSizes())
4150 state, transformOp, mixedTileSizes, getMixedTileSizes());
4155 scf::SCFTilingResult tilingResult;
4157 rewriter, state, transformOp,
target, mixedNumThreads, mixedTileSizes,
4158 getMapping(), tilingResult);
4159 if (!
diag.succeeded())
4161 if (!tilingResult.loops.empty())
4162 tileOps.push_back(tilingResult.loops.front());
4163 tiledOps.append(tilingResult.tiledOps);
4166 transformResults.
set(cast<OpResult>(getForallOp()), tileOps);
4167 transformResults.
set(cast<OpResult>(getTiledOp()), tiledOps);
4172void transform::TileUsingForallOp::getEffects(
4173 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
4183SmallVector<OpFoldResult> TileUsingForallOp::getMixedNumThreads() {
4188SmallVector<OpFoldResult> TileUsingForallOp::getMixedTileSizes() {
4193LogicalResult TileUsingForallOp::verify() {
4194 int numThreadsSpec =
static_cast<int>(!getMixedNumThreads().empty()) +
4195 static_cast<int>(getPackedNumThreads() != Value());
4196 if (numThreadsSpec > 1)
4198 "num_threads and packed_num_threads are mutually exclusive");
4199 int tileSizesSpec =
static_cast<int>(!getMixedTileSizes().empty()) +
4200 static_cast<int>(getPackedTileSizes() != Value());
4201 if (tileSizesSpec > 1)
4203 "tile_sizes and packed_tile_sizes are mutually exclusive");
4204 if (numThreadsSpec == 0 && tileSizesSpec == 0)
4205 return emitOpError(
"either (packed_)num_threads or (packed_)tile_sizes "
4206 "must be specified");
4214void transform::VectorizeChildrenAndApplyPatternsOp::build(
4215 OpBuilder &builder, OperationState &
result, Value
target,
4216 bool foldTypeExtensionsIntoContract,
bool vectorizePadding,
4217 bool vectorizeExtract,
bool flatten1DDepthwiseConv) {
4219 if (foldTypeExtensionsIntoContract) {
4221 VectorizeChildrenAndApplyPatternsOp::
4222 getFoldTypeExtensionsIntoContractAttrName(
result.name),
4225 if (vectorizePadding) {
4227 VectorizeChildrenAndApplyPatternsOp::getVectorizePaddingAttrName(
4231 if (vectorizeExtract) {
4233 VectorizeChildrenAndApplyPatternsOp::getVectorizeNdExtractAttrName(
4237 if (flatten1DDepthwiseConv) {
4239 VectorizeChildrenAndApplyPatternsOp::getFlatten_1dDepthwiseConvAttrName(
4249struct VectorizationPattern :
public RewritePattern {
4250 explicit VectorizationPattern(MLIRContext *context,
4251 bool vectorizeExtract =
false,
4252 bool flattenConv =
false)
4253 : RewritePattern(MatchAnyOpTypeTag(), 1, context),
4254 vectorizeNDExtract(vectorizeExtract),
4255 flatten1DDepthwiseConv(flattenConv) {}
4256 LogicalResult matchAndRewrite(Operation *op,
4257 PatternRewriter &rewriter)
const override {
4260 "Unsupported Op, cannot vectorize");
4261 FailureOr<VectorizationResult> vectorResults =
4263 {}, vectorizeNDExtract,
4264 flatten1DDepthwiseConv);
4265 if (
failed(vectorResults))
4267 rewriter.
replaceOp(op, vectorResults->replacements);
4274 bool vectorizeNDExtract =
false;
4278 bool flatten1DDepthwiseConv =
false;
4282DiagnosedSilenceableFailure
4283transform::VectorizeChildrenAndApplyPatternsOp::applyToOne(
4284 transform::TransformRewriter &rewriter, Operation *
target,
4285 transform::ApplyToEachResultList &results,
4286 transform::TransformState &state) {
4287 if (!
target->hasTrait<OpTrait::IsIsolatedFromAbove>()) {
4288 auto diag = this->emitOpError(
"requires isolated-from-above targets");
4294 RewritePatternSet patterns(ctx);
4295 patterns.
add<VectorizationPattern>(ctx, getVectorizeNdExtract(),
4296 getFlatten_1dDepthwiseConv());
4298 if (!getDisableTransferPermutationMapLoweringPatterns())
4301 if (!getDisableMultiReductionToContractPatterns())
4306 patterns.
add<linalg::LinalgCopyVTRForwardingPattern,
4307 linalg::LinalgCopyVTWForwardingPattern>(ctx,
4309 vector::TransferReadOp::getCanonicalizationPatterns(patterns, ctx);
4310 vector::TransferWriteOp::getCanonicalizationPatterns(patterns, ctx);
4313 patterns.
add<CopyVectorizationPattern>(ctx);
4315 if (getFoldTypeExtensionsIntoContract())
4318 if (getVectorizePadding()) {
4326 TrackingListener listener(state, *
this);
4329 GreedyRewriteConfig().setListener(&listener))))
4330 return emitDefaultDefiniteFailure(
target);
4340DiagnosedSilenceableFailure transform::VectorizeOp::apply(
4341 transform::TransformRewriter &rewriter,
4342 mlir::transform::TransformResults &transformResults,
4343 mlir::transform::TransformState &state) {
4345 if (std::empty(targets))
4347 auto transformOp = cast<TransformOpInterface>(getOperation());
4348 SmallVector<int64_t> vectorSizes;
4350 state, transformOp, getMixedVectorSizes(), vectorSizes);
4355 for (Operation *
target : targets) {
4358 <<
"Unsupported Op, cannot vectorize";
4360 FailureOr<VectorizationResult> vectorResults =
4362 getVectorizeNdExtract().value_or(
false),
4364 getAssumeDynamicDimsMatchVecSizes().value_or(
false),
4365 getCreateNamedContraction().value_or(
false));
4366 if (
failed(vectorResults)) {
4368 <<
"Attempted to vectorize, but failed";
4376void transform::VectorizeOp::getEffects(
4377 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
4383SmallVector<OpFoldResult> VectorizeOp::getMixedVectorSizes() {
4388LogicalResult transform::VectorizeOp::verify() {
4389 if (getStaticVectorSizes().size() != getScalableSizes().size())
4390 return emitOpError(
"expected same number of vector sizes (")
4391 << getStaticVectorSizes().size() <<
") and scalable sizes ("
4392 << getScalableSizes().size() <<
")";
4400DiagnosedSilenceableFailure
4401transform::HoistRedundantVectorTransfersOp::applyToOne(
4402 transform::TransformRewriter &rewriter, func::FuncOp
target,
4403 transform::ApplyToEachResultList &results,
4404 transform::TransformState &state) {
4417DiagnosedSilenceableFailure
4418transform::HoistRedundantVectorBroadcastsOp::applyToOne(
4419 transform::TransformRewriter &rewriter, mlir::Operation *
target,
4420 transform::ApplyToEachResultList &results,
4421 transform::TransformState &state) {
4432DiagnosedSilenceableFailure transform::ConvertConv2DToImg2ColOp::applyToOne(
4433 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4434 transform::ApplyToEachResultList &results,
4435 transform::TransformState &state) {
4437 auto maybeTransformed =
4440 .Case([&](linalg::Conv2DNhwcHwcfOp op) {
4443 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4446 .Case([&](linalg::DepthwiseConv2DNhwcHwcOp op) {
4449 .Case([&](linalg::Conv2DNchwFchwOp op) {
4452 .Default([&](Operation *op) {
4455 if (
failed(maybeTransformed))
4456 return emitDefaultSilenceableFailure(
target);
4458 results.
push_back(maybeTransformed->first);
4460 results.
push_back(maybeTransformed->second);
4468DiagnosedSilenceableFailure transform::FlattenElementwiseLinalgOp::applyToOne(
4469 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4470 transform::ApplyToEachResultList &results,
4471 transform::TransformState &state) {
4475 <<
"only elementwise flattening is supported";
4477 if (!llvm::all_of(
target.getIndexingMapsArray(), [](AffineMap m) {
4478 return m.isPermutation() || m.getNumResults() == 0;
4482 <<
"broadcasting of non scalar operands is not supported";
4486 if (
target.getNumLoops() <= 1) {
4493 if (
auto broadcastOp = dyn_cast<linalg::BroadcastOp>(
target.getOperation());
4494 broadcastOp && broadcastOp.getInput().getType().getRank() != 0) {
4501 std::iota(reassociation.begin(), reassociation.end(), 0);
4502 auto maybeFlattened =
4504 if (
failed(maybeFlattened))
4506 <<
"attempted to flatten, but failed";
4507 results.
push_back(maybeFlattened->collapsedOp);
4516DiagnosedSilenceableFailure transform::TransposeConv2DOp::applyToOne(
4517 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4518 transform::ApplyToEachResultList &results,
4519 transform::TransformState &state) {
4521 auto maybeTransformed =
4523 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4526 .Case([&](linalg::Conv2DNhwcFhwcQOp op) {
4529 .Default([&](Operation *op) {
4532 if (
failed(maybeTransformed))
4533 return emitDefaultSilenceableFailure(
target);
4543DiagnosedSilenceableFailure transform::TransposeMatmulOp::applyToOne(
4544 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4545 transform::ApplyToEachResultList &results,
4546 transform::TransformState &state) {
4548 bool transposeLHS = getInputToTranspose() == TransposeMatmulInput::lhs;
4549 auto maybeTransformed =
4551 .Case([&](linalg::MatmulOp op) {
4554 .Case([&](linalg::BatchMatmulOp op) {
4557 .Default(failure());
4558 if (
failed(maybeTransformed))
4568template <
typename OpTy>
4569static DiagnosedSilenceableFailure
4573 static_assert(llvm::is_one_of<OpTy, tensor::InsertSliceOp,
4574 tensor::ParallelInsertSliceOp>() &&
4577 if (
auto copySource =
4578 target.getSource().template getDefiningOp<linalg::CopyOp>()) {
4586 if (isa<mlir::ParallelCombiningOpInterface>(
target.getOperation()))
4589 Value extracted = tensor::ExtractSliceOp::create(
4592 Value copied = linalg::CopyOp::create(rewriter,
target.getLoc(),
4593 target.getSource(), extracted)
4605DiagnosedSilenceableFailure transform::InsertSliceToCopyOp::applyToOne(
4606 transform::TransformRewriter &rewriter, Operation *targetOp,
4607 transform::ApplyToEachResultList &results,
4608 transform::TransformState &state) {
4611 if (
auto target = dyn_cast<tensor::InsertSliceOp>(targetOp))
4612 return doit(rewriter,
target, results, state);
4613 if (
auto target = dyn_cast<tensor::ParallelInsertSliceOp>(targetOp))
4614 return doit(rewriter,
target, results, state);
4616 DiagnosedSilenceableFailure diag =
4617 emitSilenceableError()
4618 <<
"only InsertSliceOp and ParallelInsertSliceOp ops are supported";
4627DiagnosedSilenceableFailure transform::MapCopyToThreadsOp::applyToOne(
4628 transform::TransformRewriter &rewriter, Operation *
target,
4629 transform::ApplyToEachResultList &results,
4630 transform::TransformState &state) {
4632 if (!isa<linalg::CopyOp, tensor::PadOp>(
target)) {
4633 DiagnosedSilenceableFailure diag =
4634 emitSilenceableError()
4635 <<
"only linalg.copy and tensor.pad target ops are supported";
4639 assert(
target->getNumResults() == 1 &&
"expected single result");
4640 auto resultShapedType = cast<ShapedType>(
target->getResult(0).getType());
4641 if (!resultShapedType.hasStaticShape()) {
4642 DiagnosedSilenceableFailure diag =
4643 emitSilenceableError()
4644 <<
"only statically sized ops of rank <= 3 are supported";
4650 int64_t desiredBitAlignment = getDesiredBitAlignment();
4651 int64_t eltBitwidth =
4652 resultShapedType.getElementType().getIntOrFloatBitWidth();
4653 if (desiredBitAlignment % eltBitwidth != 0) {
4654 desiredBitAlignment = eltBitwidth;
4657 gpu::CopyMappingInfo mapping(
4659 getTotalNumThreads(),
4660 desiredBitAlignment,
4661 resultShapedType.getShape(),
4664 resultShapedType.getElementType().getIntOrFloatBitWidth());
4665 if (mapping.status == gpu::CopyMappingInfo::Status::Invalid) {
4666 DiagnosedSilenceableFailure diag =
4667 emitSilenceableError()
4668 <<
"too few threads to map copy op to threads on the most minor "
4669 "dimension, given alignment and vector size constraints, try "
4670 "smaller tile size of mapping to more threads";
4677 scf::SCFTilingResult tilingResult;
4684 ArrayRef<OpFoldResult>{},
4685 b.getArrayAttr(mapping.threadMapping),
4690 results.
push_back(tilingResult.loops.front());
4691 for (
auto *op : tilingResult.tiledOps)
4700DiagnosedSilenceableFailure transform::WinogradConv2DOp::applyToOne(
4701 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4702 transform::ApplyToEachResultList &results,
4703 transform::TransformState &state) {
4705 FailureOr<Operation *> maybeTransformed = failure();
4707 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4712 .Default([&](Operation *op) {
return false; });
4715 return emitSilenceableError()
4716 <<
"this operation is not supported to convert to Winograd Conv2D";
4719 if (
failed(maybeTransformed)) {
4720 return emitSilenceableError() <<
"apply Winograd Conv2D failed";
4727DiagnosedSilenceableFailure transform::DecomposeWinogradOp::applyToOne(
4728 transform::TransformRewriter &rewriter, Operation *
target,
4729 transform::ApplyToEachResultList &results,
4730 transform::TransformState &state) {
4732 FailureOr<Operation *> maybeTransformed = failure();
4735 .Case([&](linalg::WinogradFilterTransformOp op) {
4739 .Case([&](linalg::WinogradInputTransformOp op) {
4743 .Case([&](linalg::WinogradOutputTransformOp op) {
4750 DiagnosedSilenceableFailure diag =
4751 emitSilenceableError()
4752 <<
"this operation is not supported to decompose into other operations";
4757 if (
failed(maybeTransformed)) {
4758 DiagnosedSilenceableFailure diag =
4759 emitSilenceableError() <<
"decompose Winograd operations failed";
4768#include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOpsEnums.cpp.inc"
4770#define GET_OP_CLASSES
4771#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)
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
std::optional< Attribute > getInherentAttr(StringRef name)
Access an inherent attribute by name: returns an empty optional if there is no inherent attribute wit...
Location getLoc()
The source location the operation was defined or derived from.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
operand_type_range getOperandTypes()
result_type_range getResultTypes()
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
user_range getUsers()
Returns a range of all users.
result_range getOpResults()
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
unsigned getNumResults()
Return the number of results held by this operation.
bool has_value() const
Returns true if we contain a valid ParseResult value.
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void eraseBlock(Block *block)
This method erases all operations in a block.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
virtual void replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
void replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
Type front()
Return first type in the range.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
bool use_empty() const
Returns true if this value has no uses.
Type getType() const
Return the type of this value.
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
user_range getUsers() const
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
State for analysis-enabled bufferization.
Operation * getOwner() const
Return the owner of this operand.
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Returns a composed AffineApplyOp by composing map and operands with other AffineApplyOps supplying th...
SmallVector< OpFoldResult > makeComposedFoldedMultiResultAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Variant of makeComposedFoldedAffineApply suitable for multi-result maps.
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
LogicalResult analyzeOp(Operation *op, OneShotAnalysisState &state, BufferizationStatistics *statistics=nullptr)
Analyze op and its nested ops.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
FailureOr< PackingResult > buildPackingLoopNest(RewriterBase &rewriter, tensor::PadOp opToHoist, scf::ForOp outermostEnclosingForOp, ArrayRef< int64_t > transposeVector)
Build the packing loop nest required to hoist opToHoist above outermostEnclosingForOp.
void populateDataLayoutPropagationPatterns(RewritePatternSet &patterns, const ControlPropagationFn &controlPackUnPackPropagation, bool PoisonPaddingOk=false)
Patterns to bubble up or down data layout ops across other operations.
LogicalResult rewriteAsPaddedOp(RewriterBase &rewriter, LinalgOp opToPad, const LinalgPaddingOptions &options, LinalgOp &paddedOp, SmallVector< Value > &replacements, SmallVector< tensor::PadOp > &padOps)
Pad the iterator dimensions options.paddingDimensions of all opToPad operands to a static bounding bo...
FailureOr< std::pair< Operation *, Operation * > > rewriteInIm2Col(RewriterBase &rewriter, linalg::Conv2DNhwcHwcfOp convOp)
Convert linalg.conv_2d_nhwc_hwcf into linalg.generic (for img2col packing) and linalg....
bool hasVectorizationImpl(Operation *)
Return true if there's dedicated logic in the Linalg Vectorizer to vectorize this Op,...
void populateExtractSliceSinkingPatterns(RewritePatternSet &patterns, const ControlPropagationFn &controlPackUnPackPropagation)
Patterns to sink extract slice across other operations.
FailureOr< Operation * > decomposeWinogradFilterTransformOp(RewriterBase &rewriter, linalg::WinogradFilterTransformOp op)
Rewrite linalg.winograd_filter_transform.
std::optional< Value > allocateWorkgroupMemory(OpBuilder &builder, memref::SubViewOp subview, ArrayRef< Value > sizeBounds, DataLayout &)
Allocate the subview in the GPU workgroup memory.
FailureOr< PackTransposeResult > packTranspose(RewriterBase &rewriter, linalg::PackOp packOp, linalg::LinalgOp linalgOp, linalg::UnPackOp maybeUnPackOp, ArrayRef< int64_t > outerPerm, ArrayRef< int64_t > innerPerm)
Transpose a single PackOp -> LinalgOp -> UnPackOp chain and return the transposed PackOp -> LinalgOp ...
Value bufferizeToAllocation(RewriterBase &rewriter, const BufferizeToAllocationOptions &options, tensor::PadOp padOp, Attribute memorySpace={}, Operation *insertionPoint=nullptr)
Materialize a buffer allocation for the given tensor.pad op and lower the op to linalg....
FailureOr< VectorizationResult > vectorize(RewriterBase &rewriter, Operation *op, ArrayRef< int64_t > inputVectorSizes={}, ArrayRef< bool > inputScalableVecDims={}, bool vectorizeNDExtract=false, bool flatten1DDepthwiseConv=false, bool assumeDynamicDimsMatchVecSizes=false, bool createNamedContraction=false)
Returns a VectorizationResult containing the results of the vectorized op, or failure if the transfor...
FailureOr< Value > hoistPaddingOnTensors(RewriterBase &rewriter, tensor::PadOp opToHoist, int64_t numLoops, ArrayRef< int64_t > transposeVector, tensor::PadOp &hoistedOp, SmallVectorImpl< TransposeOp > &transposeOps)
Mechanically hoist padding operations on tensors by numLoops into a new, generally larger tensor.
FailureOr< LowerUnPackOpResult > lowerUnPack(RewriterBase &rewriter, linalg::UnPackOp unPackOp, bool lowerUnpadLikeWithExtractSlice=true)
Rewrite pack as empty + transpose + reshape + extract_slice + copy.
void populatePadOpVectorizationPatterns(RewritePatternSet &patterns, PatternBenefit baseBenefit=1)
Populates patterns with patterns that vectorize tensor.pad.
void populateLinalgTilingCanonicalizationPatterns(RewritePatternSet &patterns)
Canonicalization patterns relevant to apply after tiling patterns.
LogicalResult deallocateGPUPrivateMemory(OpBuilder &, Value)
In case of GPU private memory there is no need to deallocate since the memory is freed when going out...
FailureOr< Operation * > decomposeWinogradOutputTransformOp(RewriterBase &rewriter, linalg::WinogradOutputTransformOp op)
Rewrite linalg.winograd_output_transform.
std::function< SplitReductionOptions(LinalgOp op)> ControlSplitReductionFn
Function signature to control reduction splitting.
std::optional< Value > allocateGPUPrivateMemory(OpBuilder &builder, memref::SubViewOp subview, ArrayRef< Value > sizeBounds, DataLayout &)
Allocate the subview in the GPU private memory.
FailureOr< Operation * > rewriteInDestinationPassingStyle(RewriterBase &rewriter, tensor::FromElementsOp fromElementsOp)
Rewrite tensor.from_elements to linalg.generic.
FailureOr< Operation * > winogradConv2D(RewriterBase &rewriter, linalg::Conv2DNhwcFhwcOp op, WinogradConv2DFmr fmr)
Convert linalg.conv_2d_nhwc_fhwc to Winograd Conv2D algorithm F(m x m, r x r).
FailureOr< Operation * > transposeConv2D(RewriterBase &rewriter, linalg::Conv2DNhwcFhwcOp op)
Convert linalg.conv_2d_nhwc_fhwc(_q) to linalg.conv_2d_nhwc_hwcf(_q) by materializing transpose.
void populateFoldUnitExtentDimsPatterns(RewritePatternSet &patterns, ControlDropUnitDims &options)
Patterns to fold unit-extent dimensions in operands/results of linalg ops on tensors and memref.
LogicalResult copyToWorkgroupMemory(OpBuilder &b, Value src, Value dst)
Create Memref copy operations and add gpu barrier guards before and after the copy operation to ensur...
FailureOr< LinalgOp > generalizeNamedOp(RewriterBase &rewriter, LinalgOp linalgOp, bool emitCategoryOps=false)
Create a GenericOp or CategoryOp from the given named operation linalgOp and replace the given linalg...
LogicalResult linalgOpAnchoredEmptyTensorEliminationStep(RewriterBase &rewriter, Operation *op, bufferization::OneShotAnalysisState &state)
Try to eliminate tensor::EmptyOps inside op that are anchored on a LinalgOp.
FailureOr< Operation * > transposeBatchMatmul(RewriterBase &rewriter, linalg::BatchMatmulOp op, bool transposeLHS=true)
Pattern to replace.
FailureOr< LinalgOp > specializeGenericOp(RewriterBase &rewriter, GenericOp genericOp, bool emitCategoryOps=false)
Replace the given GenericOp with a namedOp or categoryOp.
LogicalResult promoteSubviewsPrecondition(Operation *op, LinalgPromotionOptions options)
Promote memref.subviews feeding linalg-on-buffers operations.
LogicalResult copyToGPUPrivateMemory(OpBuilder &b, Value src, Value dst)
Normal copy to between src and dst.
bool isElementwise(LinalgOp op)
Check if a LinalgOp is an element-wise operation.
FailureOr< GenericOp > interchangeGenericOp(RewriterBase &rewriter, GenericOp genericOp, ArrayRef< unsigned > interchangeVector)
Interchange the iterator_types and iterator_maps dimensions and adapts the index accesses of op.
FailureOr< StaticMultiSizeSpecification > computeStaticMultiTileSizes(LinalgOp op, unsigned dimension, int64_t targetSize, int64_t divisor)
void populateDecomposePackUnpackPatterns(RewritePatternSet &patterns)
Populates patterns to decompose linalg.pack and linalg.unpack Ops into e.g.
void populateEraseUnusedOperandsAndResultsPatterns(RewritePatternSet &patterns)
Pattern to remove dead operands and results of linalg.generic operations.
FailureOr< ContinuousTileSizeSpecification > computeContinuousTileSizes(OpBuilder &builder, TilingInterface op, unsigned dimension, OpFoldResult targetSize, bool emitAssertions)
FailureOr< StaticContinuousTileSizeSpecification > computeStaticContinuousTileSizes(LinalgOp op, unsigned dimension, unsigned targetSize)
FailureOr< SplitReductionResult > splitReduction(RewriterBase &b, LinalgOp op, const ControlSplitReductionFn &controlSplitReductionFn, bool useAlloc=false)
void populateFoldPackUnpackIntoTensorEmptyPatterns(RewritePatternSet &patterns)
Populates patterns with patterns that fold operations like linalg.pack and linalg....
void populateFoldIntoPackAndUnpackPatterns(RewritePatternSet &patterns, const ControlFoldIntoPackUnpackFn &controlFn=nullptr)
Populates patterns with patterns that fold operations like tensor.pad and tensor.extract_slice into t...
void hoistRedundantVectorBroadcasts(RewriterBase &rewriter, Operation *root)
Hoist vector.extract/vector.broadcast pairs out of immediately enclosing scf::ForOp iteratively,...
FailureOr< PackResult > packMatmulGreedily(RewriterBase &rewriter, LinalgOp linalgOp, ArrayRef< OpFoldResult > mnkPackedSizes, ArrayRef< int64_t > mnkPaddedSizesNextMultipleOf, ArrayRef< int64_t > mnkOrder)
Pack a LinalgOp by greedily inferring matmul dimensions (m, n, k) where m and n are proper parallel d...
FailureOr< PackResult > pack(RewriterBase &rewriter, linalg::LinalgOp linalgOp, ArrayRef< OpFoldResult > packedSizes)
Implement packing of a single LinalgOp by packedSizes.
void populateEraseUnnecessaryInputsPatterns(RewritePatternSet &patterns)
Patterns to promote inputs to outputs and remove unused inputs of linalg.generic ops.
std::function< bool(OpOperand *opOperand)> ControlPropagationFn
Function type which is used to control propagation of linalg.pack/unpack ops.
void populateSwapExtractSliceWithFillPatterns(RewritePatternSet &patterns)
Adds patterns that waps tensor.extract_slice(linalg.fill(cst, init)) into linalg.fill(cst,...
FailureOr< LinalgOp > promoteSubViews(OpBuilder &b, LinalgOp op, const LinalgPromotionOptions &options)
Promote the subViews into a new buffer allocated at the insertion point b.
LogicalResult deallocateWorkgroupMemory(OpBuilder &, Value)
In case of GPU group memory there is no need to deallocate.
FailureOr< Operation * > transposeMatmul(RewriterBase &rewriter, linalg::MatmulOp op, bool transposeLHS=true)
Convert Linalg matmul ops to transposed variants.
FailureOr< CollapseResult > collapseOpIterationDims(LinalgOp op, ArrayRef< ReassociationIndices > foldedIterationDims, RewriterBase &rewriter)
Collapses dimensions of linalg.generic/linalg.copy operation.
void hoistRedundantVectorTransfers(Operation *root, bool verifyNonZeroTrip=false)
Hoist vector.transfer_read/vector.transfer_write on buffers pairs out of immediately enclosing scf::F...
FailureOr< Operation * > decomposeWinogradInputTransformOp(RewriterBase &rewriter, linalg::WinogradInputTransformOp op)
Rewrite linalg.winograd_input_transform.
void populateDecomposePadPatterns(RewritePatternSet &patterns)
Populates patterns to decompose tensor.pad into e.g.
void populateFoldAddIntoDestPatterns(RewritePatternSet &patterns)
Pattern to replace linalg.add when destination passing on a contraction op suffices for achieving the...
std::pair< TilingInterface, TilingInterface > splitOp(RewriterBase &rewriter, TilingInterface op, unsigned dimension, OpFoldResult splitPoint)
Split the given op into two parts along the given iteration space dimension at the specified splitPoi...
FailureOr< SplitReductionResult > splitReductionByScaling(RewriterBase &b, LinalgOp op, const ControlSplitReductionFn &controlSplitReductionFn, bool useAlloc=false)
Scaling-based implementation of the split reduction transformation.
FailureOr< MultiSizeSpecification > computeMultiTileSizes(OpBuilder &builder, LinalgOp op, unsigned dimension, OpFoldResult targetSize, OpFoldResult divisor, bool emitAssertions=true)
Emits the IR computing the multi-sized tiling specification with two tile sizes not exceeding targetS...
FailureOr< LinalgOp > downscaleSizeOneWindowedConvolution(RewriterBase &rewriter, LinalgOp op)
Rewrite convolution/pooling/depthwise ops with size-1 window dimensions into lower-dimensional ops.
FailureOr< LowerPackResult > lowerPack(RewriterBase &rewriter, linalg::PackOp packOp, bool lowerPadLikeWithInsertSlice=true)
Rewrite pack as pad + reshape + transpose.
ForOp getForInductionVarOwner(Value val)
Returns the loop parent of an induction variable.
void populateMergeConsecutiveInsertExtractSlicePatterns(RewritePatternSet &patterns)
Collects patterns to merge consecutive tensor.insert_slice/extract_slice into one.
void populateBubbleUpExtractSliceOpPatterns(RewritePatternSet &patterns)
Appends patterns that are used to bubble up tensor.extract slice op above its producer.
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given tensor value.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
LogicalResult getOrCreateDestinations(OpBuilder &b, Location loc, Operation *op, SmallVector< Value > &result)
This is a helper function for DestinationStyleOpInterface.
void populateFoldTensorSubsetIntoVectorTransferPatterns(RewritePatternSet &patterns)
Appends patterns for folding tensor subset ops into vector transfer ops.
detail::poison_attr_matcher m_Poison()
Matches a poison constant (any attribute implementing PoisonAttrInterface).
void populateVectorTransferPermutationMapLoweringPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect a set of transfer read/write lowering patterns that simplify the permutation map (e....
void populateFoldArithExtensionPatterns(RewritePatternSet &patterns)
Collect a set of patterns that fold arithmetic extension on floating point into vector contract for t...
void populateSinkVectorOpsPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Patterns that remove redundant Vector Ops by re-ordering them with e.g.
void populateVectorReductionToContractPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect patterns to convert reduction op to vector.contract and fold transpose/broadcast ops into the...
void populateVectorStepLoweringPatterns(RewritePatternSet &patterns, unsigned indexBitwidth=64, PatternBenefit benefit=1)
Populate the pattern set with the following patterns:
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
@ PartialReductionOuterReduction
@ PartialReductionOuterParallel
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
SmallVector< InnerTileAlignment > convertInnerTileAlignments(ArrayRef< int64_t > alignments)
Maps a validated inner_tile_alignments integer array onto the per-dimension InnerTileAlignment hints ...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
DiagnosedSilenceableFailure emitSilenceableFailure(Location loc, const Twine &message={})
Emits a silenceable failure with the given message.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Attribute parseAttribute(llvm::StringRef attrStr, MLIRContext *context, Type type={}, size_t *numRead=nullptr, bool isKnownNullTerminated=false)
This parses a single MLIR attribute to an MLIR context if it was valid.
llvm::SetVector< T, Vector, Set, N > SetVector
DiagnosedDefiniteFailure emitDefiniteFailure(Location loc, const Twine &message={})
Emits a definite failure with the given message.
LogicalResult verifyInnerTileAlignments(Operation *op, ArrayRef< int64_t > alignments)
Verifies that every entry of a raw inner_tile_alignments integer array is a valid InnerTileAlignment,...
FailureOr< SCFTileAndFuseResult > tileConsumerAndFuseProducersUsingSCF(RewriterBase &rewriter, TilingInterface consumer, const SCFTileAndFuseOptions &options)
Method to tile and fuse a sequence of operations, by tiling the consumer and fusing its producers.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
llvm::TypeSwitch< T, ResultT > TypeSwitch
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
SmallVector< int64_t, 2 > ReassociationIndices
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
SmallVector< IntTy > extractFromIntegerArrayAttr(Attribute attr)
Extract integer values from the assumed ArrayAttr of IntegerAttr.
llvm::function_ref< Fn > function_ref
bool isPermutationVector(ArrayRef< int64_t > interchange)
Method to check if an interchange vector is a permutation.
bool isOneInteger(OpFoldResult v)
Return true if v is an IntegerAttr with value 1.
FailureOr< SCFTilingResult > tileUsingSCF(RewriterBase &rewriter, TilingInterface op, const SCFTilingOptions &options)
Method to tile an op that implements the TilingInterface using scf.for for iterating over the tiles.
This class represents a listener that may be used to hook into various actions within an OpBuilder.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Represents a range (offset, size, and stride) where each element of the triple may be dynamic or stat...
A listener that forwards all notifications to another listener.
ForwardingListener(OpBuilder::Listener *listener)
Container for result values of tiling.
SmallVector< Value > tiledValues
Options for analysis-enabled bufferization.
Transformation to drop unit-extent dimensions from linalg.generic operations.