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"
62template <
typename PatternTy,
typename... Args>
65 using OpTy =
typename llvm::function_traits<
66 decltype(&PatternTy::returningMatchAndRewrite)>::template arg_t<0>;
67 auto op = dyn_cast<OpTy>(operation);
72 PatternTy pattern(operation->
getContext(), std::forward<Args>(args)...);
77 auto result = pattern.returningMatchAndRewrite(op, rewriter);
80 return cast<LinalgOp>(
result->getOperation());
90 if (
auto attr = dyn_cast<Attribute>(ofr)) {
91 if (!isa<IntegerAttr>(attr))
92 return transformOp.emitDefiniteFailure() <<
"expected IntegerAttr";
97 Value transformValue = cast<Value>(ofr);
98 if (isa<TransformParamTypeInterface>(transformValue.
getType())) {
100 if (params.size() != 1)
101 return transformOp.emitDefiniteFailure()
102 <<
"requires exactly one parameter associated";
103 result.push_back(params[0]);
108 if (!llvm::hasSingleElement(payloadOps)) {
110 transformOp.emitSilenceableError()
111 <<
"handle must be mapped to exactly one payload op";
113 <<
"mapped to " << llvm::range_size(payloadOps) <<
" payload ops";
120 transformOp.emitSilenceableError()
121 <<
"payload op must have exactly 1 index result";
141 if (isa<TransformParamTypeInterface>(packedHandle.
getType())) {
143 for (
auto param : params) {
144 if (!isa<IntegerAttr>(param))
145 return transformOp.emitDefiniteFailure()
146 <<
"expected the parameter to be associated with an integer "
154 if (op->getNumResults() != 1 || !op->getResult(0).getType().isIndex()) {
156 transformOp.emitSilenceableError()
157 <<
"payload op must have exactly 1 index result";
158 diag.attachNote(op->getLoc())
159 <<
"has " << op->getNumResults() <<
" results";
162 result.push_back(op->getResult(0));
176 if (
auto attr = dyn_cast<Attribute>(paramOrHandle)) {
177 reified.push_back(cast<IntegerAttr>(attr).getInt());
180 if (isa<TransformParamTypeInterface>(
181 cast<Value>(paramOrHandle).
getType())) {
183 if (params.size() != 1)
184 return transformOp.emitSilenceableError() <<
"expected a single param";
186 cast<IntegerAttr>(params.front()).getValue().getSExtValue());
190 Value handle = cast<Value>(paramOrHandle);
191 if (!isa<TransformHandleTypeInterface>(handle.getType()))
192 return transformOp.emitSilenceableError() <<
"unexpected value handle";
194 if (!llvm::hasSingleElement(payload))
195 return transformOp.emitSilenceableError()
196 <<
"requires param or handle that is mapped to 1 payload op";
198 Operation *paramOrHandlePayloadOp = *payload.begin();
201 return transformOp.emitSilenceableError()
202 <<
"requires param or handle to be result of op with 1 index "
208 return transformOp.emitSilenceableError()
209 <<
"requires param or handle to be the result of a constant like "
212 reified.push_back(attr.getInt());
221void transform::ApplyEraseUnnecessaryInputsPatternsOp::populatePatterns(
226void transform::ApplyDecomposeTensorPackUnpackPatternsOp::populatePatterns(
231void transform::ApplyDecomposeTensorPadPatternsOp::populatePatterns(
236void transform::ApplyFoldUnitExtentDimsViaReshapesPatternsOp::populatePatterns(
242void transform::ApplyFoldUnitExtentDimsViaSlicesPatternsOp::populatePatterns(
245 options.rankReductionStrategy =
250void transform::ApplyTilingCanonicalizationPatternsOp::populatePatterns(
255void transform::ApplyFoldAddIntoDestPatternsOp::populatePatterns(
260void transform::ApplyPadVectorizationPatternsOp::populatePatterns(
265void transform::ApplyFoldIntoPackAndUnpackPatternsOp::populatePatterns(
270void transform::ApplyFoldPackUnpackIntoEmptyPatternsOp::populatePatterns(
275void transform::ApplyDataLayoutPropagationPatternsOp::populatePatterns(
284void transform::ApplyExtractSliceSinkingPatternsOp::populatePatterns(
288 Operation *producer = opOperand->get().getDefiningOp();
289 Operation *consumer = opOperand->getOwner();
304 SmallVector<Operation *> getNewOps()
const {
305 return SmallVector<Operation *>(newOps.begin(), newOps.end());
309 void notifyOperationInserted(Operation *op,
310 OpBuilder::InsertPoint previous)
override {
311 ForwardingListener::notifyOperationInserted(op, previous);
313 if (previous.
isSet())
317 assert(
inserted.second &&
"expected newly created op");
320 void notifyOperationErased(Operation *op)
override {
321 ForwardingListener::notifyOperationErased(op);
322 op->
walk([&](Operation *op) { newOps.erase(op); });
334 llvm::scope_exit resetListener(
335 [&]() { rewriter.
setListener(previousListener); });
336 NewOpsListener newOpsListener(previousListener);
340 if (getMemcpyOp() ==
"bufferization.materialize_in_destination") {
341 options.memcpyOp = linalg::BufferizeToAllocationOptions::MemcpyOp::
342 MaterializeInDestination;
343 }
else if (getMemcpyOp() ==
"memref.copy") {
346 }
else if (getMemcpyOp() ==
"linalg.copy") {
350 llvm_unreachable(
"invalid memcpy op");
352 if (getAllocOp() ==
"memref.alloc") {
355 }
else if (getAllocOp() ==
"memref.alloca") {
359 llvm_unreachable(
"invalid alloc op");
361 options.bufferizeDestinationOnly = getBufferizeDestinationOnly();
362 options.emitDealloc = getEmitDealloc();
366 getMemorySpace().has_value() ? getMemorySpace().value() :
Attribute();
373 <<
"failed to bufferize operation";
374 diag.attachNote(op->
getLoc()) <<
"target payload op";
377 allocatedBuffers.push_back(buffer);
381 results.
setValues(cast<OpResult>(getAllocatedBuffer()), allocatedBuffers);
382 results.
set(cast<OpResult>(getNewOps()), newOpsListener.getNewOps());
386void transform::BufferizeToAllocationOp::getEffects(
388 if (getBufferizeDestinationOnly()) {
399LogicalResult transform::BufferizeToAllocationOp::verify() {
400 if (getMemcpyOp() !=
"bufferization.materialize_in_destination" &&
401 getMemcpyOp() !=
"memref.copy" && getMemcpyOp() !=
"linalg.copy")
403 if (getAllocOp() !=
"memref.alloc" && getAllocOp() !=
"memref.alloca")
416 auto linalgOp = dyn_cast<linalg::LinalgOp>(operand.
getOwner());
423 Value blockArgument = linalgOp.getMatchingBlockArgument(&operand);
431 if (!isa<TensorType, FloatType, IntegerType>(value.
getType()))
433 return llvm::any_of(value.
getUses(),
443 auto type = dyn_cast<RankedTensorType>(
tensor.getType());
445 return emitSilenceableError() <<
"non-tensor type: " <<
tensor;
459 for (
auto [pos, dim] : llvm::enumerate(type.getShape())) {
460 if (!ShapedType::isDynamic(dim))
465 tensor::DimOp::create(rewriter,
tensor.getLoc(),
tensor, cst);
466 preservedOps.insert(dimOp);
467 dynamicDims.push_back(dimOp);
469 auto allocation = bufferization::AllocTensorOp::create(
470 rewriter,
tensor.getLoc(), type, dynamicDims);
472 if (getMemorySpaceAttr())
473 allocation.setMemorySpaceAttr(getMemorySpaceAttr());
474 Value allocated = allocation;
478 if (needsMaterialization) {
479 auto copy = bufferization::MaterializeInDestinationOp::create(
481 preservedOps.insert(
copy);
482 promoted.push_back(
copy.getResult());
484 promoted.push_back(allocated);
488 results.
setValues(cast<OpResult>(getPromoted()), promoted);
492void transform::PromoteTensorOp::getEffects(
508 FailureOr<linalg::LinalgOp> res =
510 if (succeeded(res)) {
514 return emitDefaultSilenceableFailure(
target);
528 auto decomposableOp = dyn_cast<AggregatedOpInterface>(
target);
529 if (!decomposableOp) {
531 "payload is not a decomposable op"));
532 return emitDefaultSilenceableFailure(
target);
535 FailureOr<SmallVector<Value>> maybeNewResults =
536 decomposableOp.decomposeOperation(rewriter);
537 if (
failed(maybeNewResults))
538 return emitDefaultSilenceableFailure(
target);
540 rewriter.
replaceOp(decomposableOp, *maybeNewResults);
541 for (
Value val : *maybeNewResults) {
542 Operation *definition = val.getDefiningOp();
553void transform::EliminateLinalgOpAnchoredEmptyTensorsOp::getEffects(
560transform::EliminateLinalgOpAnchoredEmptyTensorsOp::apply(
564 options.allowReturnAllocsFromLoops =
true;
570 <<
"failed to analyze op";
572 rewriter,
target, state)))
574 <<
"failed to eliminate LinalgOp anchored tensor.empty ops";
587 bool applyCleanup,
bool useForall) {
589 builder,
result, loopTypes,
595 applyCleanup, useForall);
601 bool applyCleanup,
bool useForall) {
609 applyCleanup, useForall);
616 bool applyCleanup,
bool useForall) {
620 build(builder,
result, loopTypes,
target, mixedTileSizes,
621 mixedTileInterchange, applyCleanup, useForall);
628 bool applyCleanup,
bool useForall) {
635 staticTileInterchange);
640 auto staticTileInterchangeAttr =
642 unsigned numExpectedLoops =
643 useForall ? 1 : staticTileSizes.size() - llvm::count(staticTileSizes, 0);
645 resultTypes.reserve(numExpectedLoops);
646 assert((loopTypes.size() == 1 || loopTypes.size() == numExpectedLoops) &&
647 "expected one loop type or as many as loops");
648 if (loopTypes.size() == 1)
649 resultTypes.append(numExpectedLoops, loopTypes[0]);
651 llvm::append_range(resultTypes, loopTypes);
656 dynamicTileInterchange,
659 staticTileInterchangeAttr,
667template <
typename Range>
672 function_ref<FailureOr<scf::SCFTileAndFuseResult>(TilingInterface)>
676 size_t numTargets = llvm::range_size(payloadOps);
679 auto tilingInterfaceOp = dyn_cast<TilingInterface>(
target);
680 if (!tilingInterfaceOp)
681 return transformOp->
emitError(
"only TilingInterface ops are supported");
684 FailureOr<scf::SCFTileAndFuseResult> tiledResults =
685 applyFn(tilingInterfaceOp);
686 if (failed(tiledResults))
691 llvm::append_range(opsToReplace, tiledResults->fusedProducers);
692 for (
Operation *toReplace : opsToReplace) {
693 for (
OpResult res : toReplace->getResults())
694 if (
auto replacement = tiledResults->replacements.lookup(res))
696 if (toReplace->use_empty()) {
702 tiledLinalgOps.push_back(tiledResults->tiledAndFusedOps.front());
703 assert(tiledResults->loops.size() == numLoops &&
704 "Mismatched number of loops, tile and fuse transform should have "
706 for (
unsigned int i = 0; i < numLoops; ++i)
707 loopOps[i].
push_back(tiledResults->loops[i]);
710 transformResults.
set(transformOp->
getOpResult(0), tiledLinalgOps);
719 for (
unsigned int idx = 0; idx < numTargets; ++idx)
720 for (
unsigned int i = 0; i < numLoops; ++i)
721 flattenedLoopOps.push_back(loopOps[i][idx]);
722 transformResults.
set(transformOp->
getOpResult(1), flattenedLoopOps);
724 for (
unsigned int i = 0; i < numLoops; ++i)
725 transformResults.
set(transformOp->
getOpResult(i + 1), loopOps[i]);
735 auto transformOp = cast<TransformOpInterface>(getOperation());
741 state, transformOp, mixedTileSizes, getPackedTileSizes())
743 state, transformOp, mixedTileSizes, getMixedTileSizes());
748 state, transformOp, getMixedTileInterchange(), tileInterchange);
752 scf::SCFTilingOptions tilingOptions;
753 tilingOptions.interchangeVector = tileInterchange;
754 bool useForall = getUseForall();
755 tilingOptions.setLoopType(useForall
756 ? scf::SCFTilingOptions::LoopType::ForallOp
757 : scf::SCFTilingOptions::LoopType::ForOp);
758 tilingOptions = tilingOptions.setTileSizes(mixedTileSizes);
759 scf::SCFTileAndFuseOptions tileAndFuseOptions;
760 tileAndFuseOptions.tilingOptions = tilingOptions;
763 tileAndFuseOptions.tilingOptions.setInnerTileAlignments(
766 if (getApplyCleanup()) {
769 tensor::ExtractSliceOp::getCanonicalizationPatterns(patterns, context);
772 tileAndFuseOptions.cleanupPatterns = std::move(patterns);
779 numLoops = llvm::count_if(mixedTileSizes, [](
OpFoldResult ofr) {
780 auto attr = dyn_cast<Attribute>(ofr);
783 return cast<IntegerAttr>(attr).getInt() != 0;
787 rewriter, getOperation(), state.
getPayloadOps(getTarget()), numLoops,
788 transformResults, getPackedTileSizes() !=
nullptr,
789 [&](TilingInterface tilingInterfaceOp)
790 -> FailureOr<scf::SCFTileAndFuseResult> {
798LogicalResult transform::FuseOp::verify() {
799 bool hasPackedTiles = getPackedTileSizes() !=
nullptr;
800 if (!getMixedTileSizes().empty() && hasPackedTiles)
802 "tile_sizes and packed_tile_sizes are mutually exclusive");
804 auto iterspace_rank = getStaticTileSizes().size();
806 if (permutation.size() > iterspace_rank)
808 <<
"interchange length exceeds iteration space dimensions ("
809 << iterspace_rank <<
"), found " << getTileInterchange();
811 for (
int64_t v : permutation) {
812 if (!ShapedType::isDynamic(v)) {
813 if (v < 0 || v >=
static_cast<int64_t>(iterspace_rank))
814 return emitOpError() <<
"expects interchange values to be in range [0, "
815 << iterspace_rank <<
"), found: " << v;
817 return emitOpError() <<
"found duplicate interchange value: " << v;
823 size_t numExpectedLoops = getUseForall() || hasPackedTiles
825 : sizes.size() - llvm::count(sizes, 0);
826 if (numExpectedLoops != getNumResults() - 1)
827 return emitOpError() <<
"expects " << numExpectedLoops <<
" loop results";
837 return getMixedValues(getStaticTileInterchange(), getTileInterchange(),
841void transform::FuseOp::getEffects(
855void transform::FuseIntoContainingOp::build(
OpBuilder &builder,
858 Value containingOp) {
859 result.addOperands({producerOp, containingOp});
860 auto resultType = transform::AnyOpType::get(builder.
getContext());
861 result.addTypes({resultType, resultType});
877 (domInfo.
dominates(containingOp, user))) {
878 dominatedUsers.insert(user);
881 if (dominatedUsers.empty())
885 auto forallOp = cast<scf::ForallOp>(containingOp);
891 auto genericOp = dyn_cast<linalg::GenericOp>(producerOp);
896 newOuts.push_back(outputs[resultNumber]);
899 auto newforallOp = scf::ForallOp::create(
900 rewriter, loc, forallOp.getMixedLowerBound(),
901 forallOp.getMixedUpperBound(), forallOp.getMixedStep(), newOuts,
902 forallOp.getMapping());
904 newforallOp.getRegion().takeBody(forallOp.getRegion());
909 newforallOp.getBody()->addArgument(newOuts.back().getType(),
910 newOuts.back().getLoc());
911 auto bbArgs = newforallOp.getBody()->getArguments();
914 Operation *op = use.getOwner();
915 return newforallOp->isProperAncestor(op);
919 scf::InParallelOp terminatorOp = newforallOp.getTerminator();
921 terminatorOp.getYieldingOps(), [](
Operation &op) { return &op; });
922 Operation *firstYieldOp = yieldingOps.front();
925 Value dst = newforallOp.getRegionIterArgs().back();
927 tensor::ParallelInsertSliceOp::create(rewriter, firstYieldOp->
getLoc(), src,
928 dst, offsets, sizes, strides);
930 for (
auto result : llvm::enumerate(forallOp.getResults())) {
932 newforallOp->getResult(
result.index()));
935 newforallOp->getResults().back(),
937 Operation *user = use.getOwner();
938 return dominatedUsers.contains(user);
952 destWorklist.push_back(dst);
954 while (!destWorklist.empty()) {
955 Value currentDst = destWorklist.pop_back_val();
959 if (src == currentDst)
964 auto bbArg = dyn_cast<BlockArgument>(currentDst);
968 Block *parentBlock = bbArg.getOwner();
969 assert(parentBlock &&
"unlinked block argument");
972 assert(parentOp &&
"expected block argument with parent operation");
975 auto parentLoop = dyn_cast<LoopLikeOpInterface>(parentOp);
979 for (
auto innerIterArg : parentLoop.getRegionIterArgs()) {
981 OpOperand *operand = parentLoop.getTiedLoopInit(innerIterArg);
982 Value loopBlockArgument =
984 destWorklist.push_back(loopBlockArgument);
997static std::tuple<SmallVector<Operation *>,
Operation *>
1001 LDBG() <<
"Try to fuse a direct extract use";
1002 auto tileableProducer = dyn_cast<TilingInterface>(producerOp);
1003 if (!tileableProducer) {
1005 <<
"producer is not a TileableInterface: " << *producerOp;
1012 auto it = llvm::find_if(tileableProducer->getUsers(), [&](
Operation *user) {
1013 auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user);
1014 return sliceOp && containingOp->isProperAncestor(sliceOp);
1018 if (it == tileableProducer->getUsers().end()) {
1019 diag.attachNote(tileableProducer->getLoc())
1020 <<
"could not find fusion opportunity for: " << *tileableProducer;
1023 auto sliceOpToTile = cast<tensor::ExtractSliceOp>(*it);
1036 if (LoopLikeOpInterface containerLoop =
1037 dyn_cast<LoopLikeOpInterface>(sliceOpToTile->getParentOp())) {
1043 auto dpsInterface = dyn_cast<DestinationStyleOpInterface>(
clone);
1047 for (
OpOperand &initOperandPtr : dpsInterface.getDpsInitsMutable()) {
1048 Value producerOperand =
1049 clone->getOperand(initOperandPtr.getOperandNumber());
1051 containerLoop.getRegionIterArgs()) {
1052 OpOperand *bbArg = containerLoop.getTiedLoopInit(containerIterArg);
1053 Value consumerOperand =
1057 initOperandPtr.set(containerIterArg);
1063 tileableProducer = dyn_cast<TilingInterface>(
clone);
1068 cast<OpResult>(sliceOpToTile.getSource()).getResultNumber();
1069 LDBG() <<
"resultNumber: " << resultNumber;
1074 FailureOr<TilingResult> tileAndFuseResult =
1075 tileableProducer.generateResultTileValue(rewriter, resultNumber, offsets,
1076 sizes, innerTileAlignments);
1078 if (failed(tileAndFuseResult)) {
1079 diag.attachNote(tileableProducer->getLoc())
1080 <<
"failed to tile producer op: " << *tileableProducer;
1085 for (
auto *tiledOp : tileAndFuseResult->tiledOps) {
1086 LDBG() <<
"tiledProducer: " << *tiledOp;
1091 auto maybeRankReduced = tensor::ExtractSliceOp::rankReduceIfNeeded(
1092 rewriter, sliceOpToTile->getLoc(), tileAndFuseResult->tiledValues[0],
1093 cast<RankedTensorType>(sliceOpToTile->getResult(0).getType()).getShape());
1094 if (failed(maybeRankReduced)) {
1096 <<
"shape types don't match (missing canonicalization?):\nTiledOp: "
1097 << tileAndFuseResult->tiledValues[0]
1098 <<
"\nSliceOp: " << sliceOpToTile.getOperation() <<
'\n';
1101 rewriter.
replaceOp(sliceOpToTile, *maybeRankReduced);
1105 rewriter,
diag, producerOp, containingOp, *tileAndFuseResult,
1106 resultNumber, offsets, sizes);
1109 if (isa<LoopLikeOpInterface>(containingOp))
1110 rewriter.
eraseOp(tileableProducer);
1112 return std::make_tuple(tileAndFuseResult->tiledOps, newContainingOp);
1125 LDBG() <<
"Try to fuse an extract use through block argument";
1127 auto tileableProducer = dyn_cast<TilingInterface>(producerOp);
1128 if (!tileableProducer) {
1130 <<
"producer is not a TileableInterface: " << *producerOp;
1135 scf::ForallOp forallOp;
1136 auto itProducerUses =
1137 llvm::find_if(tileableProducer->getUses(), [&](
OpOperand &use) {
1138 forallOp = dyn_cast<scf::ForallOp>(use.getOwner());
1142 if (!forallOp || forallOp != containingOp) {
1143 diag.attachNote(tileableProducer->getLoc())
1144 <<
"could not find a use by the containing op: " << *tileableProducer;
1159 auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user);
1160 return sliceOp && containingOp->isProperAncestor(sliceOp);
1164 if (itBBArgUsers == bbArg.
getUsers().end()) {
1166 <<
"could not find fusion opportunity for bbArg: " << bbArg;
1169 auto sliceOpToTile = cast<tensor::ExtractSliceOp>(*itBBArgUsers);
1177 int64_t resultNumber = cast<OpResult>(pUse->
get()).getResultNumber();
1178 LDBG() <<
"resultNumber: " << resultNumber;
1183 rewriter, tileableProducer->getLoc(), tileableProducer,
1184 destinationTensors))) {
1185 diag.attachNote(tileableProducer->getLoc())
1186 <<
"failed to get destination tensors for: " << *tileableProducer;
1191 bvm.
map(destinationTensors[resultNumber], bbArg);
1192 auto tileableProducerClone =
1193 cast<TilingInterface>(rewriter.
clone(*tileableProducer, bvm));
1194 llvm::scope_exit scopeGuard(
1195 [&]() { rewriter.
eraseOp(tileableProducerClone); });
1198 FailureOr<TilingResult> tileAndFuseResult =
1199 tileableProducerClone.generateResultTileValue(
1200 rewriter, resultNumber, sliceOpToTile.getMixedOffsets(),
1201 sliceOpToTile.getMixedSizes(), innerTileAlignments);
1202 if (failed(tileAndFuseResult)) {
1203 diag.attachNote(tileableProducer->getLoc())
1204 <<
"failed to tile producer op: " << *tileableProducer;
1209 auto maybeRankReduced = tensor::ExtractSliceOp::rankReduceIfNeeded(
1210 rewriter, sliceOpToTile->getLoc(), tileAndFuseResult->tiledValues[0],
1211 cast<RankedTensorType>(sliceOpToTile->getResult(0).getType()).getShape());
1212 assert(succeeded(maybeRankReduced) &&
"unexpected shape");
1213 rewriter.
replaceOp(sliceOpToTile, *maybeRankReduced);
1218 destinationTensors.front());
1221 return tileAndFuseResult->tiledOps;
1227 LDBG() <<
"Try to fuse an use by cloning";
1234 uses.push_back(&use);
1239 if (containingOp == use.getOwner()) {
1241 <<
"producer op use by containing op cannot be fused by cloning";
1249 diag.attachNote(producerOp->
getLoc()) <<
"no fusion opportunity by cloning";
1258 assert(!isa<tensor::ParallelInsertSliceOp>(use->
getOwner()) &&
1259 "Parallel insert slice is not a valid clone destination");
1260 unsigned resultNumber = cast<OpResult>(use->
get()).getResultNumber();
1261 LDBG() <<
"resultNumber: " << resultNumber;
1265 fusedOp = rewriter.
clone(*producerOp);
1267 use->
getOwner(), [&] { use->set(fusedOp->getOpResult(resultNumber)); });
1272bool transform::FuseIntoContainingOp::allowsRepeatedHandleOperands() {
1277LogicalResult transform::FuseIntoContainingOp::verify() {
1287 auto containingOps = state.
getPayloadOps(getContainingOp());
1288 if (!llvm::hasSingleElement(containingOps)) {
1290 <<
"requires exactly one containing_op handle (got "
1291 << llvm::range_size(containingOps) <<
")";
1293 Operation *containingOp = *containingOps.begin();
1302 if (std::empty(producerOps)) {
1304 results.
set(cast<OpResult>(getNewContainingOp()), {containingOp});
1311 auto getNextProducer = [&]() -> FailureOr<Operation *> {
1312 for (
const auto &it :
enumerate(remainingProducers)) {
1315 int64_t numUsesInContainingOp =
1317 return containingOp->isAncestor(op);
1322 if (numUsesInContainingOp > 0) {
1323 if (numUsesInContainingOp == 1)
1324 remainingProducers.erase(remainingProducers.begin() + it.index());
1331 while (!remainingProducers.empty()) {
1332 auto nextProducer = getNextProducer();
1333 if (
failed(nextProducer)) {
1335 <<
"could not find next producer to fuse into container";
1336 diag.attachNote(containingOp->
getLoc()) <<
"containing op";
1344 diag <<
"could not fuse " << *producerOp <<
" into " << *containingOp;
1352 rewriter,
diag, producerOp, containingOp, innerTileAlignments);
1353 if (!tiledOps.empty()) {
1354 LDBG() <<
"\nFused a direct extract use\n" << *containingOp;
1355 fusedOps.append(tiledOps);
1356 if (newContainingOp) {
1364 LogicalResult replacementStatus =
1367 (
void)replacementStatus;
1368 assert(succeeded(replacementStatus) &&
1369 "unable to update transform state mapping");
1370 rewriter.
eraseOp(containingOp);
1371 containingOp = newContainingOp;
1378 rewriter,
diag, producerOp, containingOp, innerTileAlignments);
1379 if (!tiledContainingOpOperand.empty()) {
1380 LDBG() <<
"\nFused an extract use through block argument\n"
1382 fusedOps.append(tiledContainingOpOperand);
1389 LDBG() <<
"\nFused an use by cloning\n" << *containingOp;
1390 fusedOps.push_back(cloned);
1396 results.
set(cast<OpResult>(getFusedOp()), fusedOps);
1397 results.
set(cast<OpResult>(getNewContainingOp()), {containingOp});
1401void transform::FuseIntoContainingOp::getEffects(
1419 if (isa<GenericOp>(
target)) {
1425 if (succeeded(generic)) {
1426 results.
push_back(generic->getOperation());
1429 return emitDefaultSilenceableFailure(
target);
1442 if (!isa<GenericOp>(
target)) {
1449 FailureOr<LinalgOp> named =
1451 if (succeeded(named)) {
1452 results.
push_back(named->getOperation());
1455 return emitDefaultSilenceableFailure(
target);
1469 if (interchangeVector.empty()) {
1474 unsigned numLoops = cast<LinalgOp>(
target.getOperation()).getNumLoops();
1475 if (interchangeVector.size() != numLoops) {
1476 return emitSilenceableError()
1477 << getIteratorInterchangeAttrName() <<
" has length ("
1478 << interchangeVector.size()
1479 <<
") different from the number of loops in the target operation ("
1490LogicalResult transform::InterchangeOp::verify() {
1492 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, permutation.size()));
1493 if (!std::is_permutation(sequence.begin(), sequence.end(),
1494 permutation.begin(), permutation.end())) {
1496 <<
"expects iterator_interchange to be a permutation, found "
1497 << getIteratorInterchange();
1512 if (!isa<linalg::CopyOp>(targetOp)) {
1514 emitSilenceableError() <<
"only linalg.copy target ops are supported";
1515 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1519 auto copyOp = dyn_cast<linalg::CopyOp>(targetOp);
1520 if (!copyOp.hasPureBufferSemantics()) {
1522 emitSilenceableError()
1523 <<
"cannot transform a linalg.copy on tensors into a memref.copy";
1524 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1530 assert(inputs.size() == 1 &&
"expected linalg copy op with one input");
1531 assert(outputs.size() == 1 &&
"expected memref copy op with one output");
1532 Value input = inputs.front();
1533 Value output = outputs.front();
1538 if (!isa<ShapedType>(input.
getType())) {
1540 emitSilenceableError()
1541 <<
"cannot transform a linalg.copy which input has no shape";
1542 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1547 assert(isa<ShapedType>(output.
getType()));
1549 if (cast<ShapedType>(input.
getType()).getElementType() !=
1550 cast<ShapedType>(output.
getType()).getElementType()) {
1552 emitSilenceableError()
1553 <<
"cannot transform a linalg.copy with different source and "
1554 "destination element types ";
1555 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1576 bool lowerPadLikeWithInsertSlice = getLowerPadLikeWithInsertSlice();
1577 FailureOr<LowerPackResult> res =
1581 <<
"cannot lower to pad + expand + transpose";
1584 transformResults.
push_back(res->expandShapeOp);
1585 transformResults.
push_back(res->transposeOp);
1598 bool lowerUnpadLikeWithExtractSlice = getLowerUnpadLikeWithExtractSlice();
1599 FailureOr<LowerUnPackOpResult> res =
1603 emitSilenceableError()
1604 <<
"cannot lower to transpose + collapse + extract";
1605 diag.attachNote(
target->getLoc()) <<
"target payload op";
1608 transformResults.
push_back(res->emptyOp);
1609 transformResults.
push_back(res->transposeOp);
1610 transformResults.
push_back(res->collapseShapeOp);
1611 transformResults.
push_back(res->extractSliceOp);
1612 transformResults.
push_back(res->copyOp);
1623 result.addAttribute(MatchOp::getOpsAttrName(
result.name),
1632 result.addAttribute(MatchOp::getOpsAttrName(
result.name),
1634 result.addTypes(resultTypes);
1642 if (getOps().has_value())
1643 strs.insert_range(getOps()->getAsValueRange<StringAttr>());
1646 if (!llvm::hasSingleElement(payloadOps)) {
1651 bool incorrectNumOperandTypes =
false;
1658 if (getInterface().has_value()) {
1659 auto iface = getInterface().value();
1660 if (iface == transform::MatchInterfaceEnum::LinalgOp &&
1663 if (iface == transform::MatchInterfaceEnum::TilingInterface &&
1664 !isa<TilingInterface>(op))
1666 if (iface == transform::MatchInterfaceEnum::LoopLikeInterface &&
1667 !isa<LoopLikeOpInterface>(op))
1672 if (getOpAttrs().has_value()) {
1673 DictionaryAttr opAttrs = getOpAttrs().value();
1675 if (attr.getName() == getInterfaceAttrName() ||
1676 attr.getName() == getOpsAttrName())
1678 if (!op->
hasAttr(attr.getName()))
1680 if (op->
getAttr(attr.getName()) != attr.getValue())
1685 if (getFilterResultType().has_value()) {
1686 Type t = getFilterResultType().value();
1691 if (getFilterOperandTypes().has_value()) {
1692 mlir::ArrayAttr types = getFilterOperandTypes().value();
1695 if (types.size() == 1) {
1698 dyn_cast<mlir::TypeAttr>(getFilterOperandTypes().value()[0]);
1699 Type t = cast<::mlir::Type>(typeattr.getValue());
1701 [&](
Type operandType) { return operandType == t; }))
1706 if (types.size() != operandTypes.size()) {
1707 incorrectNumOperandTypes =
true;
1711 for (
auto [attr, operandType] :
1712 llvm::zip_equal(getFilterOperandTypes().value(), operandTypes)) {
1713 auto typeattr = cast<mlir::TypeAttr>(attr);
1714 Type type = cast<::mlir::Type>(typeattr.getValue());
1716 if (type != operandType)
1727 (*payloadOps.begin())->walk(matchFun);
1728 if (incorrectNumOperandTypes)
1730 "type, then it must contain as much types as "
1731 "the number of operands in the target ops");
1732 results.
set(cast<OpResult>(getResult()), res);
1747 Type &targetType,
Type &lowSizeType,
1749 Type &splitPointType) {
1750 FunctionType funcType;
1752 if (failed(parser.
parseType<FunctionType>(funcType)))
1755 if (funcType.getNumInputs() != 1 || funcType.getNumResults() != 1) {
1756 parser.
emitError(typeLoc) <<
"expects a trailing functional type with one "
1757 "argument and one result";
1759 targetType = funcType.getInput(0);
1760 lowSizeType = highSizeType = splitPointType = funcType.getResult(0);
1768 if (isa<TransformParamTypeInterface>(getLowSize().
getType())) {
1769 if (
target.hasDynamicShape()) {
1770 auto diag = emitSilenceableError()
1771 <<
"cannot compute parametric tile sizes for dynamically "
1772 "shaped payload op";
1773 diag.attachNote(
target->getLoc()) <<
"payload op";
1778 target, getDimension(), getTargetSize(), getDivisor());
1780 return emitSilenceableError()
1781 <<
"failed to compute multi-size tiling sizes";
1785 results.
assign(llvm::map_range(
1787 spec->lowTileSize * spec->lowTripCount}),
1788 [&builder,
this](
int64_t value) {
1800 builder,
target, getDimension(), targetSize, divisor);
1802 return emitSilenceableError() <<
"could not generate tile size computation";
1809 {spec->lowTileSize, spec->lowTripCount});
1810 Operation *lowTileSize = spec->lowTileSize.getDefiningOp();
1811 Operation *highTileSize = spec->highTileSize.getDefiningOp();
1812 assert(lowTileSize && highTileSize && splitPoint &&
1813 "tile sizes are not produced by operations");
1821void transform::MultiTileSizesOp::getEffects(
1825 if (isa<TransformParamTypeInterface>(getLowSize().
getType()))
1831LogicalResult transform::MultiTileSizesOp::verify() {
1834 return emitOpError() <<
"expects all results type to be the same";
1853 Type linalgOpHType = transform::OperationType::get(
1854 builder.
getContext(), GenericOp::getOperationName());
1873 if (std::empty(targetOps)) {
1874 transformResults.
set(cast<OpResult>(getPackedOp()),
1879 auto linalgOp = dyn_cast<LinalgOp>(*targetOps.begin());
1880 if (!llvm::hasSingleElement(targetOps) || !linalgOp) {
1881 return emitSilenceableError()
1882 <<
"requires target to map to exactly 1 LinalgOp (got "
1883 << llvm::range_size(targetOps) <<
")";
1886 if (getMixedPackedSizes().size() != linalgOp.getNumLoops()) {
1887 return emitSilenceableError()
1888 <<
"requires number of packed sizes match the number of loops ("
1889 << getMixedPackedSizes().size() <<
" vs " << linalgOp.getNumLoops()
1896 state, *
this, packedSizes, getMixedPackedSizes());
1899 FailureOr<PackResult> maybeResult =
pack(rewriter, linalgOp, packedSizes);
1903 transformResults.
set(cast<OpResult>(getPackedOp()),
1904 {maybeResult->packedLinalgOp.getOperation()});
1908void transform::PackOp::getEffects(
1920LogicalResult transform::PackGreedilyOp::verify() {
1922 return emitOpError() << getMatmulInnerDimsOrderAttrName()
1923 <<
" is not a valid permutation";
1926 if (!getMatmulPaddedSizesNextMultipleOf().empty()) {
1927 for (
auto [s, nmo] :
1928 llvm::zip_equal(getMixedMatmulPackedSizes(),
1929 getMatmulPaddedSizesNextMultipleOf())) {
1932 (!maybeStaticPackedSize.has_value() || *maybeStaticPackedSize != 0)) {
1933 return emitOpError() <<
"at most one of the packed_size and the "
1934 "padded_sizes_next_multiple_of can be nonzero "
1935 "for the matmul strategy";
1948 auto linalgOp = dyn_cast<LinalgOp>(op);
1959 getMixedMatmulPackedSizes(),
1961 getMatmulPaddedSizesNextMultipleOf(),
1962 getMatmulInnerDimsOrder());
1963 if (succeeded(packResult)) {
1964 results.push_back(packResult->packedLinalgOp);
1967 results.push_back(linalgOp);
1969 transformResults.
set(cast<OpResult>(getPackedOp()), results);
1975 return getMixedValues(getStaticMatmulPackedSizes(), getMatmulPackedSizes(),
1979void transform::PackGreedilyOp::getEffects(
1991LogicalResult transform::PackTransposeOp::verify() {
1994 <<
" is not a valid permutation";
1998 <<
" is not a valid permutation";
2000 if (getInnerPerm().empty() && getOuterPerm().empty()) {
2001 return emitOpError() <<
" at least one of " << getInnerPermAttrName()
2002 <<
" or " << getOuterPermAttrName()
2003 <<
" must be specified";
2009enum class OuterOrInnerPerm { Outer = 0, Inner = 1 };
2019template <
typename RelayoutOpTy>
2020static bool isValidPackingPermutation(
2022 OuterOrInnerPerm outerOrInnerPerm = OuterOrInnerPerm::Outer) {
2024 llvm::is_one_of<RelayoutOpTy, linalg::PackOp, linalg::UnPackOp>::value,
2025 "applies to only pack or unpack operations");
2026 if (!op || permutation.empty())
2028 size_t innerRank = op.getInnerDimsPos().size();
2029 if (outerOrInnerPerm == OuterOrInnerPerm::Inner)
2033 if (std::is_same<RelayoutOpTy, linalg::PackOp>::value) {
2034 return permutation.size() == op.getSourceRank() &&
2037 return permutation.size() == op.getDestRank() &&
2045 auto packOrUnpackOps = state.
getPayloadOps(getTargetPackOrUnPackOp());
2048 if (std::empty(packOrUnpackOps)) {
2049 transformResults.
set(cast<OpResult>(getPackedOp()), {});
2050 transformResults.
set(cast<OpResult>(getPackOp()), {});
2051 transformResults.
set(cast<OpResult>(getUnPackOp()), {});
2057 if (!llvm::hasSingleElement(packOrUnpackOps) ||
2058 !llvm::hasSingleElement(linalgOps)) {
2059 return emitSilenceableError()
2060 <<
"requires target to map to exactly 1 "
2061 "packing op and 1 packed op ("
2062 <<
"got " << llvm::range_size(packOrUnpackOps) <<
" and "
2063 << llvm::range_size(linalgOps) <<
")";
2067 auto packOp = dyn_cast<linalg::PackOp>(*packOrUnpackOps.begin());
2068 auto unPackOp = dyn_cast<linalg::UnPackOp>(*packOrUnpackOps.begin());
2069 if ((!packOp && !unPackOp)) {
2070 return emitSilenceableError() <<
"requires target to map to a "
2071 "linalg.pack or linalg.unpack";
2073 LinalgOp linalgOpTarget = dyn_cast<LinalgOp>(*linalgOps.begin());
2074 if (!linalgOpTarget)
2075 return emitSilenceableError() <<
"requires a LinalgOp target";
2079 if (packOp && packOp.getResult().hasOneUse())
2080 linalgOp = dyn_cast<LinalgOp>(*(packOp.getResult().getUsers().begin()));
2082 linalgOp = unPackOp.getSource().getDefiningOp<LinalgOp>();
2083 if (linalgOp != linalgOpTarget) {
2085 packOp ? StringLiteral{
"not a single use by the LinalgOp target"}
2086 : StringLiteral{
"not produced by the LinalgOp target"};
2087 return emitSilenceableError() << errorMsg;
2093 assert(!packOp &&
"packOp must be null on entry when unPackOp is not null");
2094 OpOperand *packUse = linalgOp.getDpsInitOperand(
2095 cast<OpResult>(unPackOp.getSource()).getResultNumber());
2097 if (!packOp || !packOp.getResult().hasOneUse())
2098 return emitSilenceableError() <<
"could not find matching pack op";
2102 for (
auto permType : {OuterOrInnerPerm::Outer, OuterOrInnerPerm::Inner}) {
2104 (permType == OuterOrInnerPerm::Outer) ? getOuterPerm() : getInnerPerm();
2105 auto errorMsg = (permType == OuterOrInnerPerm::Outer)
2106 ? StringLiteral{
"invalid outer_perm"}
2107 : StringLiteral{
"invalid inner_perm"};
2108 if (!isValidPackingPermutation(packOp, perm, permType) ||
2109 !isValidPackingPermutation(unPackOp, perm, permType)) {
2111 unPackOp ? unPackOp.getOperation() : packOp.getOperation();
2112 return emitSilenceableError() << errorMsg <<
": " << *packOrUnpackOp;
2118 assert(packOp && linalgOp &&
"unexpected null op");
2122 rewriter, packOp, linalgOp, unPackOp, getOuterPerm(), getInnerPerm());
2124 assert(succeeded(res) &&
"unexpected packTranspose failure");
2127 transformResults.
set(cast<OpResult>(getPackOp()), {res->transposedPackOp});
2128 transformResults.
set(cast<OpResult>(getPackedOp()),
2129 {res->transposedLinalgOp});
2131 transformResults.
set(cast<OpResult>(getUnPackOp()),
2132 {res->transposedUnPackOp});
2134 transformResults.
set(cast<OpResult>(getUnPackOp()), {});
2149 StringRef copyBackOp,
2150 bool usePrescribedTensorShapes) {
2151 auto resultType = transform::AnyOpType::get(
b.getContext());
2157 b.getI64ArrayAttr(paddingDimensions),
2160 (padToMultipleOf.empty()
2162 :
b.getDenseI64ArrayAttr(padToMultipleOf)),
2163 b.getI64ArrayAttr(nofoldFlags),
2164 b.getArrayAttr(transposePaddings),
2165 b.getStringAttr(copyBackOp),
2167 usePrescribedTensorShapes ?
b.getUnitAttr() :
nullptr);
2175 StringRef copyBackOp,
2176 bool usePrescribedTensorShapes) {
2177 auto resultType = transform::AnyOpType::get(
b.getContext());
2181 staticPadToMultipleOf);
2187 b.getI64ArrayAttr(paddingDimensions),
2188 dynamicPadToMultipleOf,
2189 staticPadToMultipleOf,
2190 b.getI64ArrayAttr(nofoldFlags),
2191 b.getArrayAttr(transposePaddings),
2193 usePrescribedTensorShapes);
2196void PadOp::getEffects(
2204SmallVector<OpFoldResult> PadOp::getMixedPadToMultipleOf() {
2206 return getMixedValues(getStaticPadToMultipleOf(), getPadToMultipleOf(),
b);
2209DiagnosedSilenceableFailure
2210transform::PadOp::apply(transform::TransformRewriter &rewriter,
2211 transform::TransformResults &results,
2212 transform::TransformState &state) {
2213 auto transformOp = cast<TransformOpInterface>(getOperation());
2214 SmallVector<Operation *> paddedOps, padOps, copyBackOps;
2217 auto linalgTarget = dyn_cast<LinalgOp>(
target);
2218 if (!linalgTarget) {
2219 auto diag = emitSilenceableError() <<
"expected LinalgOp target";
2220 diag.attachNote(
target->getLoc()) <<
"target op";
2225 SmallVector<bool> nofoldFlags;
2226 for (int64_t packPadding :
2228 nofoldFlags.push_back(
static_cast<bool>(packPadding));
2231 SmallVector<Attribute> paddingValues;
2232 for (
auto const &[untypedAttr, elementOrTensorType] :
2233 llvm::zip(getPaddingValues(), linalgTarget->getOperandTypes())) {
2236 paddingValues.push_back(untypedAttr);
2239 auto attr = dyn_cast<TypedAttr>(untypedAttr);
2241 emitOpError(
"expects padding values to be typed attributes or poison");
2246 if (
auto stringAttr = dyn_cast<StringAttr>(attr)) {
2250 if (!parsedAttr || parsedAttr.getType() != elementType) {
2252 << elementType <<
", got " << untypedAttr;
2253 diag.attachNote(linalgTarget.getLoc()) <<
"when applied to this op";
2256 paddingValues.push_back(parsedAttr);
2260 if (attr.getType() != elementType) {
2262 << elementType <<
", got " << attr;
2263 diag.attachNote(linalgTarget.getLoc()) <<
"when applied to this op";
2266 paddingValues.push_back(attr);
2270 SmallVector<SmallVector<int64_t>> transposePaddings;
2271 for (Attribute transposeVector : cast<ArrayAttr>(getTransposePaddings()))
2273 cast<ArrayAttr>(transposeVector)));
2280 SmallVector<int64_t> padToMultipleOf;
2282 state, transformOp, getMixedPadToMultipleOf(), padToMultipleOf);
2285 if (padToMultipleOf.empty())
2287 SmallVector<int64_t>(
options.paddingDimensions.size(), 1);
2289 options.padToMultipleOf = padToMultipleOf;
2290 options.paddingValues = paddingValues;
2291 options.nofoldFlags = nofoldFlags;
2292 if (getCopyBackOp() ==
2293 bufferization::MaterializeInDestinationOp::getOperationName()) {
2294 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::
2295 BufferizationMaterializeInDestination;
2296 }
else if (getCopyBackOp() == linalg::CopyOp::getOperationName()) {
2297 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::LinalgCopy;
2298 }
else if (getCopyBackOp() == kCopyOpNone) {
2299 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::None;
2301 llvm_unreachable(
"unsupported copy_back op");
2304 bool irChanged =
false;
2305 if (getUsePrescribedTensorShapes() &&
2306 linalgTarget.hasPureTensorSemantics()) {
2307 OpBuilder::InsertionGuard g(rewriter);
2309 for (OpOperand &operand : linalgTarget->getOpOperands()) {
2310 for (
auto [i, dim] : llvm::enumerate(linalgTarget.getShape(&operand))) {
2311 if (ShapedType::isStatic(dim))
2313 options.setSizeToPadTo(operand.getOperandNumber(), i,
2315 operand.get().getLoc(),
2322 SmallVector<Value> replacements;
2323 SmallVector<tensor::PadOp> newPadOps;
2325 replacements, newPadOps))) {
2331 auto diag = emitSilenceableError() <<
"failed to pad op";
2332 diag.attachNote(
target->getLoc()) <<
"target op";
2341 rewriter.
replaceOp(linalgTarget, replacements);
2342 paddedOps.push_back(paddedOp);
2343 padOps.append(newPadOps.begin(), newPadOps.end());
2344 if (
options.copyBackOp != LinalgPaddingOptions::CopyBackOp::None) {
2345 for (Value v : replacements) {
2346 Operation *copyBackOp = v.getDefiningOp();
2347 if (!llvm::is_contained(copyBackOps, copyBackOp))
2348 copyBackOps.push_back(copyBackOp);
2353 results.
set(cast<OpResult>(getPadded()), paddedOps);
2354 results.
set(cast<OpResult>(getPad()), padOps);
2355 results.
set(cast<OpResult>(getCopy()), copyBackOps);
2359LogicalResult transform::PadOp::verify() {
2360 SmallVector<int64_t> nofoldFlags =
2362 if (any_of(nofoldFlags, [](int64_t packPadding) {
2363 return packPadding != 0 && packPadding != 1;
2366 <<
"expects nofold_flags to contain booleans (0/1), found "
2367 << getNofoldFlags();
2370 SmallVector<int64_t> paddingDimensions =
2372 if (any_of(paddingDimensions,
2373 [](int64_t paddingDimension) {
return paddingDimension < 0; })) {
2374 return emitOpError() <<
"expects padding_dimensions to contain positive "
2376 << getPaddingDimensions();
2378 if (!getMixedPadToMultipleOf().empty()) {
2379 if (getMixedPadToMultipleOf().size() != paddingDimensions.size()) {
2380 return emitOpError() <<
"expects as many multiples as padding_dimensions";
2383 ArrayAttr transposes = getTransposePaddings();
2384 for (Attribute attr : transposes) {
2386 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2387 if (!std::is_permutation(sequence.begin(), sequence.end(),
2388 transpose.begin(), transpose.end())) {
2390 <<
"expects transpose_paddings to be a permutation, found "
2394 if (getCopyBackOp() !=
2395 bufferization::MaterializeInDestinationOp::getOperationName() &&
2396 getCopyBackOp() != linalg::CopyOp::getOperationName() &&
2397 getCopyBackOp() != kCopyOpNone)
2406void transform::PadTilingInterfaceOp::build(OpBuilder &
b,
2409 ArrayRef<int64_t> paddingSizes,
2410 bool padToMultipleOf) {
2411 auto resultType = transform::AnyOpType::get(
b.getContext());
2420 :
b.getDenseI64ArrayAttr(paddingSizes)),
2422 padToMultipleOf ?
b.getUnitAttr() :
nullptr);
2425void transform::PadTilingInterfaceOp::build(
2427 ArrayRef<OpFoldResult> mixedPaddingSizes,
bool padToMultipleOf) {
2428 auto resultType = transform::AnyOpType::get(
b.getContext());
2429 SmallVector<int64_t> staticPaddingSizes;
2430 SmallVector<Value> dynamicPaddingSizes;
2432 staticPaddingSizes);
2438 dynamicPaddingSizes,
2443void transform::PadTilingInterfaceOp::getEffects(
2444 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2451SmallVector<OpFoldResult>
2452transform::PadTilingInterfaceOp::getMixedPaddingSizes() {
2457DiagnosedSilenceableFailure
2458transform::PadTilingInterfaceOp::apply(transform::TransformRewriter &rewriter,
2459 transform::TransformResults &results,
2460 transform::TransformState &state) {
2461 SmallVector<Operation *> paddedOps, padOps;
2464 auto targetOp = dyn_cast<TilingInterface>(
target);
2466 auto diag = emitSilenceableError() <<
"expected TilingInterface target";
2467 diag.attachNote(
target->getLoc()) <<
"target op";
2474 if (!isa<IndexingMapOpInterface>(targetOp.getOperation())) {
2475 auto diag = emitSilenceableError() <<
"only IndexingMapOpInterface ops "
2477 diag.attachNote(
target->getLoc()) <<
"target op";
2482 SmallVector<Attribute> paddingValues;
2483 for (
auto const &[untypedAttr, elementOrTensorType] :
2484 llvm::zip(getPaddingValues(), targetOp->getOperandTypes())) {
2485 auto attr = dyn_cast<TypedAttr>(untypedAttr);
2489 paddingValues.push_back(untypedAttr);
2493 emitOpError(
"expects padding values to be typed attributes or poison");
2497 if (
auto stringAttr = dyn_cast<StringAttr>(attr)) {
2501 if (!parsedAttr || parsedAttr.getType() != elementType) {
2503 << elementType <<
", got " << attr;
2504 diag.attachNote(targetOp.getLoc()) <<
"when applied to this op";
2507 paddingValues.push_back(parsedAttr);
2511 if (attr.getType() != elementType) {
2513 << elementType <<
", got " << attr;
2514 diag.attachNote(targetOp.getLoc()) <<
"when applied to this op";
2517 paddingValues.push_back(attr);
2521 PadTilingInterfaceOptions
options;
2522 options.setPaddingValues(paddingValues)
2523 .setPaddingSizes(getMixedPaddingSizes())
2524 .setPadToMultipleOf(getPadToMultipleOf());
2526 OpBuilder::InsertionGuard g(rewriter);
2529 rewriter, cast<TilingInterface>(targetOp.getOperation()),
options);
2530 if (
failed(maybePadOps)) {
2531 auto diag = emitSilenceableError() <<
"failed to pad op";
2532 diag.attachNote(
target->getLoc()) <<
"target op";
2535 const auto &[paddedOperands, paddedOp, slicedResults] = maybePadOps.value();
2538 paddedOps.push_back(paddedOp);
2539 padOps.append(paddedOperands.begin(), paddedOperands.end());
2540 rewriter.
replaceOp(targetOp.getOperation(), slicedResults);
2543 results.
set(cast<OpResult>(getPadded()), paddedOps);
2544 results.
set(cast<OpResult>(getPad()), padOps);
2548LogicalResult transform::PadTilingInterfaceOp::verify() {
return success(); }
2554DiagnosedSilenceableFailure transform::HoistPadBuildPackingLoopNestOp::apply(
2555 transform::TransformRewriter &rewriter,
2556 transform::TransformResults &transformResults,
2557 transform::TransformState &state) {
2560 if (!llvm::hasSingleElement(targetOps) || !llvm::hasSingleElement(loopOps)) {
2562 <<
"requires exactly one target and one loop handle (got "
2563 << llvm::range_size(targetOps) <<
" and "
2564 << llvm::range_size(loopOps) <<
")";
2567 auto padOp = dyn_cast_or_null<tensor::PadOp>(*targetOps.begin());
2568 auto loopOp = dyn_cast_or_null<scf::ForOp>(*loopOps.begin());
2569 if (!padOp || !loopOp)
2572 FailureOr<linalg::detail::PackingResult>
result =
2578 if (
result->clonedLoopIvs.empty()) {
2579 transformResults.
set(cast<OpResult>(getPackingLoop()),
2580 {
result->hoistedPadOp.getOperation()});
2583 auto outerPackedLoop =
2585 transformResults.
set(cast<OpResult>(getPackingLoop()),
2586 {outerPackedLoop.getOperation()});
2590LogicalResult transform::HoistPadBuildPackingLoopNestOp::verify() {
2591 ArrayRef<int64_t> transpose = getTranspose();
2592 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2593 if (!std::is_permutation(sequence.begin(), sequence.end(), transpose.begin(),
2595 return emitOpError() <<
"expects transpose to be a permutation, found "
2601void transform::HoistPadBuildPackingLoopNestOp::getEffects(
2602 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2609DiagnosedSilenceableFailure
2610transform::HoistPadOp::applyToOne(transform::TransformRewriter &rewriter,
2612 transform::ApplyToEachResultList &results,
2613 transform::TransformState &state) {
2614 tensor::PadOp hoistedPadOp;
2615 SmallVector<TransposeOp> transposeOps;
2616 FailureOr<Value>
result =
2618 hoistedPadOp, transposeOps);
2629 return emitDefaultSilenceableFailure(
target);
2632LogicalResult transform::HoistPadOp::verify() {
2633 ArrayRef<int64_t> transpose = getTranspose();
2634 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2635 if (!std::is_permutation(sequence.begin(), sequence.end(), transpose.begin(),
2637 return emitOpError() <<
"expects transpose to be a permutation, found "
2647DiagnosedSilenceableFailure
2648transform::PromoteOp::applyToOne(transform::TransformRewriter &rewriter,
2650 transform::ApplyToEachResultList &results,
2651 transform::TransformState &state) {
2652 LinalgPromotionOptions promotionOptions;
2653 if (!getOperandsToPromote().empty())
2656 if (getUseFullTilesByDefault())
2658 getUseFullTilesByDefault());
2659 if (getUseOriginalSubviewSize())
2663 promotionOptions = promotionOptions.
setUseAlloca(getUseAlloca());
2664 if (!getUseFullTileBuffers().empty())
2666 llvm::to_vector(getUseFullTileBuffers().getAsValueRange<BoolAttr>()));
2667 if (getAlignment().has_value())
2668 promotionOptions = promotionOptions.
setAlignment(*getAlignment());
2669 if (getMemorySpace().has_value())
2670 promotionOptions = promotionOptions.
setMemorySpace(*getMemorySpace());
2672 if (getMapping().has_value()) {
2674 auto mapping = *getMapping();
2675 if (mapping.size() > 1)
2676 return emitDefaultDefiniteFailure(
target);
2678 auto addressSpace = cast<mlir::gpu::GPUMemorySpaceMappingAttr>(mapping[0]);
2680 if (addressSpace.getAddressSpace() ==
2681 mlir::gpu::GPUDialect::getWorkgroupAddressSpace()) {
2688 }
else if (addressSpace.getAddressSpace() ==
2689 mlir::gpu::GPUDialect::getPrivateAddressSpace()) {
2697 return emitDefaultDefiniteFailure(
target);
2702 return emitDefaultDefiniteFailure(
target);
2707 return emitDefaultDefiniteFailure(
target);
2716DiagnosedSilenceableFailure
2717transform::ReplaceOp::apply(transform::TransformRewriter &rewriter,
2718 TransformResults &transformResults,
2719 TransformState &state) {
2723 for (Operation *
target : payload) {
2724 if (
target->getNumOperands() > 0)
2726 if (!
target->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
2727 target->getNumRegions() > 0)
2729 <<
"expected target that is isolated from above";
2733 Operation *pattern = &getBodyRegion().front().front();
2734 SmallVector<Operation *> replacements;
2735 for (Operation *
target : payload) {
2736 if (getOperation()->isAncestor(
target))
2743 transformResults.
set(cast<OpResult>(getReplacement()), replacements);
2747void transform::ReplaceOp::getEffects(
2748 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2754LogicalResult transform::ReplaceOp::verify() {
2755 if (!getBodyRegion().hasOneBlock())
2757 if (std::distance(getBodyRegion().front().begin(),
2758 getBodyRegion().front().end()) != 1)
2759 return emitOpError() <<
"expected one operation in block";
2760 Operation *
replacement = &getBodyRegion().front().front();
2763 <<
"expected replacement without operands";
2764 if (!
replacement->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
2767 <<
"expect op that is isolated from above";
2775DiagnosedSilenceableFailure
2776transform::ScalarizeOp::applyToOne(transform::TransformRewriter &rewriter,
2778 transform::ApplyToEachResultList &results,
2779 transform::TransformState &state) {
2780 scf::SCFTilingOptions tilingOptions;
2781 tilingOptions.setTileSizeComputationFunction([&](OpBuilder &
b, Operation *) {
2782 SmallVector<OpFoldResult> tileSizes;
2783 Location loc =
target.getLoc();
2784 SmallVector<OpFoldResult> allShapeSizes =
2785 target.createFlatListOfOperandDims(
b, loc);
2786 AffineMap map =
target.getShapesToLoopsMap();
2789 SmallVector<OpFoldResult> shapeSizes =
2794 for (OpFoldResult shapeSize : shapeSizes) {
2796 :
b.getIndexAttr(1));
2801 FailureOr<scf::SCFTilingResult> maybeTilingResult =
tileUsingSCF(
2802 rewriter, cast<TilingInterface>(
target.getOperation()), tilingOptions);
2803 if (
failed(maybeTilingResult))
2804 return emitDefaultDefiniteFailure(
target);
2806 if (
target->getNumResults())
2811 results.
reserve(maybeTilingResult->tiledOps.size());
2812 for (Operation *tiled : maybeTilingResult->tiledOps)
2821DiagnosedSilenceableFailure
2822transform::ConvertToLoopsOp::apply(transform::TransformRewriter &rewriter,
2823 transform::TransformResults &results,
2824 transform::TransformState &state) {
2825 SmallVector<Operation *> loops;
2827 auto tilingOp = dyn_cast<TilingInterface>(*
target);
2829 DiagnosedSilenceableFailure
diag =
2830 emitSilenceableError()
2831 <<
"expected the payload to implement TilingInterface";
2832 diag.attachNote(
target->getLoc()) <<
"payload op";
2836 FailureOr<SmallVector<scf::ForOp>> generatedLoops =
2837 scf::lowerToLoopsUsingSCFForOp(rewriter, tilingOp);
2838 if (
failed(generatedLoops))
2839 return emitDefaultDefiniteFailure(
target);
2840 for (scf::ForOp &loop : *generatedLoops) {
2841 loops.push_back(loop.getOperation());
2845 results.
set(cast<OpResult>(getResult()), loops);
2853DiagnosedSilenceableFailure
2854transform::RewriteInDestinationPassingStyleOp::applyToOne(
2855 transform::TransformRewriter &rewriter, Operation *
target,
2856 transform::ApplyToEachResultList &results,
2857 transform::TransformState &state) {
2859 FailureOr<Operation *> maybeResult =
2861 .Case<DestinationStyleOpInterface>([](
auto op) {
return op; })
2862 .Case<tensor::FromElementsOp, tensor::GenerateOp, tensor::PadOp>(
2863 [&rewriter](
auto op) {
2867 return emitDefaultSilenceableFailure(
target);
2876DiagnosedSilenceableFailure
2877SplitOp::apply(transform::TransformRewriter &rewriter,
2878 TransformResults &results, TransformState &state) {
2880 SmallVector<Operation *> payload =
2883 bool isMultiwaySplit = getMultiway();
2885 if (isMultiwaySplit && !llvm::hasSingleElement(payload)) {
2887 <<
"requires exactly one target when "
2888 "multiway split is enabled (got "
2889 << llvm::range_size(payload) <<
")";
2892 SmallVector<OpFoldResult> chunkSizes;
2894 if (!isMultiwaySplit)
2895 chunkSizes.reserve(payload.size());
2897 if (getDynamicChunkSizes()) {
2899 if (isa<TransformHandleTypeInterface>(getDynamicChunkSizes().
getType())) {
2900 chunkSizes = llvm::map_to_vector(
2901 state.
getPayloadOps(getDynamicChunkSizes()), [&](Operation *op) {
2904 diag = emitSilenceableError()
2905 <<
"expected dynamic split point handle to point to a "
2906 "single-result index-typed op";
2907 diag.attachNote(op->
getLoc()) <<
"dynamic split point";
2912 chunkSizes = llvm::map_to_vector(
2913 state.
getParams(getDynamicChunkSizes()),
2914 [](Attribute attr) {
return OpFoldResult(attr); });
2916 if (
diag.isSilenceableFailure())
2921 if (!isMultiwaySplit && chunkSizes.size() != payload.size()) {
2923 <<
"expected the dynamic split point handle to point to as "
2925 << chunkSizes.size() <<
") as the target handle ("
2926 << payload.size() <<
")";
2929 chunkSizes.resize(payload.size(),
2933 auto checkStructuredOpAndDimensions =
2934 [&](LinalgOp linalgOp, Location loc) -> DiagnosedSilenceableFailure {
2936 auto diag = emitSilenceableError() <<
"only applies to structured ops";
2937 diag.attachNote(loc) <<
"target op";
2941 if (getDimension() >= linalgOp.getNumLoops()) {
2942 auto diag = emitSilenceableError() <<
"dimension " << getDimension()
2943 <<
" does not exist in target op";
2944 diag.attachNote(loc) <<
"target op";
2950 auto checkFailureInSplitting =
2951 [&](
bool hasFailed, Location loc) -> DiagnosedSilenceableFailure {
2960 SmallVector<Operation *> opList;
2961 if (isMultiwaySplit) {
2964 TilingInterface head, tail;
2965 Operation *
target = payload.front();
2967 LinalgOp linalgOp = dyn_cast<LinalgOp>(
target);
2970 DiagnosedSilenceableFailure
diag =
2971 checkStructuredOpAndDimensions(linalgOp,
target->getLoc());
2972 if (
diag.isSilenceableFailure())
2975 for (
auto &&[idx, chunkSize] : llvm::enumerate(chunkSizes)) {
2978 target = tail.getOperation();
2983 linalgOp = cast<LinalgOp>(
target);
2984 Location loc =
target->getLoc();
2988 rewriter, cast<TilingInterface>(linalgOp.getOperation()),
2989 getDimension(), chunkSize);
2992 DiagnosedSilenceableFailure
diag =
2993 checkFailureInSplitting(!head && !tail, loc);
2994 if (
diag.isDefiniteFailure())
2997 opList.push_back(head.getOperation());
3002 opList.push_back(tail.getOperation());
3006 SmallVector<Operation *> first, second;
3007 Operation *noSecondPart =
nullptr;
3008 for (
const auto &pair : llvm::zip(payload, chunkSizes)) {
3009 Operation *
target = std::get<0>(pair);
3010 Location loc =
target->getLoc();
3011 LinalgOp linalgOp = dyn_cast<LinalgOp>(
target);
3012 DiagnosedSilenceableFailure
diag =
3013 checkStructuredOpAndDimensions(linalgOp,
target->getLoc());
3015 if (
diag.isSilenceableFailure())
3019 std::tie(first.emplace_back(), second.emplace_back()) =
linalg::splitOp(
3020 rewriter, cast<TilingInterface>(linalgOp.getOperation()),
3021 getDimension(), std::get<1>(pair));
3024 DiagnosedSilenceableFailure diagSplit =
3025 checkFailureInSplitting(!first.back() && !second.back(), loc);
3030 if (!second.back()) {
3036 if (second.size() != first.size() && !second.empty()) {
3037 auto diag = emitSilenceableError()
3038 <<
"splitting does not produce the second part for a subset "
3041 <<
"expected splitting to produce the second part of all "
3042 "or none of the targets";
3044 <<
"first target with no second part";
3048 opList.append(first);
3049 if (!second.empty())
3050 opList.append(second);
3052 results.
set(cast<OpResult>(getSplitList()), opList);
3056void SplitOp::getEffects(
3057 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3059 if (getDynamicChunkSizes())
3065ParseResult SplitOp::parse(OpAsmParser &parser, OperationState &
result) {
3066 OpAsmParser::UnresolvedOperand
target, dynamicChunkSizes;
3067 IntegerAttr staticChunkSizes;
3071 OptionalParseResult dynamicPointParseResult =
3073 if (!dynamicPointParseResult.
has_value()) {
3074 int64_t staticChunkSizesValue;
3088 if (dynamicPointParseResult.
has_value()) {
3089 Type chunkSizesType;
3102 SplitOp::getStaticChunkSizesAttrName(
result.name).getValue(),
3104 result.addTypes(targetType);
3108void SplitOp::print(OpAsmPrinter &printer) {
3109 printer <<
" " << getTarget() <<
" after ";
3110 int64_t staticChunkSize =
static_cast<int64_t
>(getStaticChunkSizes());
3111 if (staticChunkSize != ShapedType::kDynamic)
3112 printer << staticChunkSize;
3114 printer << getDynamicChunkSizes();
3117 {getStaticChunkSizesAttrName()});
3118 printer <<
" : " << getTarget().getType();
3119 if (staticChunkSize == ShapedType::kDynamic)
3120 printer <<
", " << getDynamicChunkSizes().getType();
3123LogicalResult SplitOp::verify() {
3124 if ((
static_cast<int64_t
>(getStaticChunkSizes()) != ShapedType::kDynamic) ^
3125 (getDynamicChunkSizes() ==
nullptr)) {
3126 return emitOpError() <<
"expects either a dynamic or a static split "
3127 "point to be provided";
3136void transform::SplitReductionOp::build(
3137 OpBuilder &builder, OperationState &
result, Value
target,
3138 int64_t splitFactor, int64_t insertSplitDimension,
bool innerParallel,
3139 bool useScalingAlgorithm,
bool useAlloc) {
3142 result.addAttribute(SplitReductionOp::getSplitFactorAttrName(
result.name),
3145 SplitReductionOp::getInsertSplitDimensionAttrName(
result.name),
3147 if (innerParallel) {
3148 result.addAttribute(SplitReductionOp::getInnerParallelAttrName(
result.name),
3151 if (useScalingAlgorithm) {
3153 SplitReductionOp::getUseScalingAlgorithmAttrName(
result.name),
3157 result.addAttribute(SplitReductionOp::getUseAllocAttrName(
result.name),
3160 auto resultType = transform::AnyOpType::get(ctx);
3161 result.addTypes({resultType, resultType, resultType, resultType});
3164DiagnosedSilenceableFailure transform::SplitReductionOp::applyToOne(
3165 transform::TransformRewriter &rewriter, LinalgOp
target,
3166 transform::ApplyToEachResultList &results,
3167 transform::TransformState &state) {
3169 return linalg::SplitReductionOptions{int64_t(getSplitFactor()),
3170 unsigned(getInsertSplitDimension()),
3171 bool(getInnerParallel())};
3174 FailureOr<SplitReductionResult> splitResult =
3175 (getUseScalingAlgorithm())
3179 return emitDefaultDefiniteFailure(
target);
3181 results.
push_back(splitResult->initOrAlloc);
3183 results.
push_back(splitResult->splitLinalgOp);
3184 results.
push_back(splitResult->resultCombiningLinalgOp);
3192void transform::TileReductionUsingForOp::build(
3193 OpBuilder &builder, OperationState &
result, Value
target,
3194 ArrayRef<int64_t> staticTileSizes) {
3201 auto opTy = transform::AnyOpType::get(ctx);
3207 staticTileSizesAttr);
3210DiagnosedSilenceableFailure transform::TileReductionUsingForOp::applyToOne(
3211 transform::TransformRewriter &rewriter, Operation *
target,
3212 transform::ApplyToEachResultList &results,
3213 transform::TransformState &state) {
3216 auto partialReductionOp = dyn_cast<PartialReductionOpInterface>(
target);
3217 if (!partialReductionOp) {
3220 "Operation should implement PartialReductionOpInterface");
3223 SmallVector<unsigned> reductionDims =
3225 if (reductionDims.empty()) {
3226 for (
auto [idx, iteratorType] :
3227 llvm::enumerate(partialReductionOp.getLoopIteratorTypes())) {
3228 if (iteratorType == utils::IteratorType::reduction)
3229 reductionDims.push_back(idx);
3233 scf::SCFTilingOptions
options;
3234 options.setLoopType(scf::SCFTilingOptions::LoopType::ForOp);
3235 options.setReductionTilingStrategy(
3238 options.setReductionDims(reductionDims);
3239 FailureOr<scf::SCFTilingResult>
result =
3240 scf::tileUsingSCF(rewriter, partialReductionOp,
options);
3244 "failed to tile using partial reduction");
3247 for (Value initValue :
result->initialValues)
3249 for (
auto *parallelTiledOp :
result->tiledOps)
3251 for (
auto *mergeOp :
result->mergeOps)
3261void transform::TileReductionUsingForallOp::build(
3262 OpBuilder &builder, OperationState &
result, Value
target,
3263 ArrayRef<int64_t> staticNumThreads, ArrayRef<int64_t> staticTileSizes,
3271 auto opTy = transform::AnyOpType::get(ctx);
3278 staticNumThreadsAttr,
3279 staticTileSizesAttr,
3283DiagnosedSilenceableFailure transform::TileReductionUsingForallOp::applyToOne(
3284 transform::TransformRewriter &rewriter, Operation *
target,
3285 transform::ApplyToEachResultList &results,
3286 transform::TransformState &state) {
3289 auto partialReductionOp = dyn_cast<PartialReductionOpInterface>(
target);
3290 if (!partialReductionOp) {
3293 "Operation should implement PartialReductionOpInterface");
3295 SmallVector<OpFoldResult> numThreads =
3297 SmallVector<OpFoldResult> tileSizes =
3300 scf::SCFTilingOptions
options;
3301 options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
3302 options.setReductionTilingStrategy(
3304 if (!getNumThreads().empty()) {
3305 options.setNumThreads(numThreads);
3307 options.setTileSizes(tileSizes);
3309 if (
auto mapping = getMapping()) {
3310 options.setMapping(mapping.value().getValue());
3312 SmallVector<unsigned> reductionDims =
3314 if (reductionDims.empty()) {
3315 for (
auto [idx, iteratorType] :
3316 llvm::enumerate(partialReductionOp.getLoopIteratorTypes())) {
3317 if (iteratorType == utils::IteratorType::reduction)
3318 reductionDims.push_back(idx);
3321 options.setReductionDims(reductionDims);
3322 FailureOr<scf::SCFTilingResult>
result =
3323 scf::tileUsingSCF(rewriter, partialReductionOp,
options);
3326 auto diag = emitSilenceableError() <<
"could not tile reduction";
3331 for (Value initValue :
result->initialValues)
3333 for (
auto *parallelTiledOp :
result->tiledOps)
3335 for (
auto *mergeOp :
result->mergeOps)
3345DiagnosedSilenceableFailure
3346transform::ContinuousTileSizesOp::apply(transform::TransformRewriter &rewriter,
3347 TransformResults &transformResults,
3348 TransformState &state) {
3350 SmallVector<Operation *> targetOps =
3353 if (!llvm::hasSingleElement(targetOps)) {
3355 <<
"requires exactly one target (got " << llvm::range_size(targetOps)
3359 Operation *
target = *targetOps.begin();
3360 auto linalgOp = dyn_cast<LinalgOp>(
target);
3361 auto tileableOp = dyn_cast<TilingInterface>(
target);
3366 OpBuilder builder(linalgOp.getContext());
3368 if (isa<TransformParamTypeInterface>(getChunkSizes().
getType())) {
3369 if (linalgOp.hasDynamicShape()) {
3370 auto diag = emitSilenceableError()
3371 <<
"cannot compute parametric tile sizes for dynamically "
3372 "shaped payload op";
3373 diag.attachNote(linalgOp->getLoc()) <<
"payload op";
3377 FailureOr<StaticContinuousTileSizeSpecification> spec =
3381 return emitSilenceableError()
3382 <<
"failed to compute multi-size tiling sizes";
3385 SmallVector<int64_t> chunkSizes;
3387 for (
auto &&[tileSize, tripCount] :
3388 llvm::zip_equal(spec->tileSizes, spec->tripCounts))
3389 chunkSizes.push_back(tileSize * tripCount);
3391 auto getI64AttrsFromI64 = [&](ArrayRef<int64_t> values) {
3392 return llvm::map_to_vector(values, [&](int64_t value) -> Attribute {
3397 getI64AttrsFromI64(spec->tileSizes));
3398 transformResults.
setParams(cast<OpResult>(getChunkSizes()),
3399 getI64AttrsFromI64(chunkSizes));
3406 OpFoldResult targetSize = builder.
getIndexAttr(getTargetSize());
3407 unsigned dimension = getDimension();
3410 builder, tileableOp, dimension, targetSize,
true);
3412 return emitSilenceableError() <<
"could not generate tile size computation";
3417 auto apply = [&](AffineExpr expr, ArrayRef<OpFoldResult> ofrs) -> Value {
3422 SmallVector<Value> chunkSizes;
3424 for (
auto &&[tileSize, tripCount] :
3425 llvm::zip_equal(spec->tileSizes, spec->tripCounts)) {
3426 splitPoint = apply(s0 * s1, {tileSize, tripCount});
3427 chunkSizes.push_back(splitPoint);
3430 auto getDefiningOps = [&](ArrayRef<Value> values) {
3431 return llvm::map_to_vector(values, [&](Value value) -> Operation * {
3437 getDefiningOps(spec->tileSizes));
3438 transformResults.
set(cast<OpResult>(getChunkSizes()),
3439 getDefiningOps(chunkSizes));
3444LogicalResult transform::ContinuousTileSizesOp::verify() {
3447 return emitOpError() <<
"expects all results type to be the same";
3453void transform::ContinuousTileSizesOp::getEffects(
3454 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3471 Type &tileSizesType,
3472 Type &chunkSizesType) {
3473 FunctionType funcType;
3475 if (failed(parser.
parseType<FunctionType>(funcType)))
3478 if (funcType.getNumInputs() != 1 || funcType.getNumResults() != 1) {
3479 parser.
emitError(typeLoc) <<
"expects a trailing functional type with one "
3480 "argument and one result";
3482 targetType = funcType.getInput(0);
3483 tileSizesType = chunkSizesType = funcType.getResult(0);
3492void transform::TileUsingForOp::build(
3494 Value
target, ArrayRef<int64_t> staticTileSizes,
3495 ArrayRef<int64_t> interchange,
3496 std::optional<ArrayRef<bool>> scalableSizes) {
3497 return build(builder,
result, loopTypes,
3501 interchange, scalableSizes);
3504void transform::TileUsingForOp::build(
3505 OpBuilder &builder, OperationState &
result, Value
target,
3506 ArrayRef<int64_t> staticTileSizes, ArrayRef<int64_t> interchange,
3507 std::optional<ArrayRef<bool>> scalableSizes) {
3510 interchange, scalableSizes);
3513void transform::TileUsingForOp::build(
3514 OpBuilder &builder, OperationState &
result, Value
target,
3515 ArrayRef<OpFoldResult> mixedTileSizes, ArrayRef<int64_t> interchange,
3516 std::optional<ArrayRef<bool>> scalableSizes) {
3519 SmallVector<Type> loopTypes(1, builder.
getType<transform::AnyOpType>());
3520 build(builder,
result, loopTypes,
target, mixedTileSizes, interchange,
3524void transform::TileUsingForOp::build(
3526 Value
target, ArrayRef<OpFoldResult> mixedTileSizes,
3527 ArrayRef<int64_t> interchange,
3528 std::optional<ArrayRef<bool>> scalableSizes) {
3529 SmallVector<int64_t> staticTileSizes;
3530 SmallVector<Value> dynamicTileSizes;
3536 unsigned numExpectedLoops =
3537 staticTileSizes.size() - llvm::count(staticTileSizes, 0);
3538 SmallVector<Type> resultTypes;
3539 resultTypes.reserve(numExpectedLoops);
3540 assert((loopTypes.size() == 1 || loopTypes.size() == numExpectedLoops) &&
3541 "expected one loop type or as many as loops");
3542 if (loopTypes.size() == 1)
3543 resultTypes.append(numExpectedLoops, loopTypes[0]);
3545 llvm::append_range(resultTypes, loopTypes);
3546 SmallVector<bool> expandedScalableSizes(mixedTileSizes.size(),
false);
3547 if (scalableSizes.has_value())
3548 expandedScalableSizes.assign(scalableSizes->begin(), scalableSizes->end());
3553 staticTileSizesAttr,
3555 expandedScalableSizes);
3558LogicalResult transform::TileUsingForOp::verify() {
3560 return emitOpError(
"expected same number of sizes (")
3562 << getScalableSizes().size() <<
")";
3563 ArrayRef<int64_t> staticSizes = getStaticSizes();
3564 unsigned numExpectedLoops = staticSizes.size() - llvm::count(staticSizes, 0);
3565 if (getLoops().size() != numExpectedLoops)
3566 return emitOpError(
"expected number of loops to tile (")
3567 << numExpectedLoops <<
") to match number of `loops` results ("
3568 << getLoops().size() <<
")";
3572DiagnosedSilenceableFailure
3573transform::TileUsingForOp::apply(transform::TransformRewriter &rewriter,
3574 TransformResults &transformResults,
3575 TransformState &state) {
3576 ArrayRef<int64_t> tileSizes = getStaticSizes();
3578 SmallVector<Operation *> targets =
3580 SmallVector<SmallVector<Operation *>> dynamicSizeProducers;
3581 SmallVector<SmallVector<int64_t>> paramSizes;
3585 if (isa<TransformParamTypeInterface>(transformValue.getType())) {
3586 dynamicSizeProducers.push_back({});
3587 ArrayRef<Attribute> params = state.
getParams(transformValue);
3588 paramSizes.push_back(llvm::map_to_vector(params, [](Attribute attr) {
3589 return cast<IntegerAttr>(attr).getValue().getSExtValue();
3592 if (paramSizes.back().size() != targets.size()) {
3593 DiagnosedSilenceableFailure
diag =
3594 emitSilenceableError()
3595 <<
"expected as many parameter values ("
3596 << dynamicSizeProducers.back().size() <<
") as target ops ("
3597 << targets.size() <<
")";
3598 diag.attachNote(transformValue.getLoc()) <<
"for this parameter";
3604 paramSizes.push_back({});
3605 dynamicSizeProducers.push_back(
3608 if (dynamicSizeProducers.back().size() != targets.size()) {
3609 DiagnosedSilenceableFailure
diag =
3610 emitSilenceableError()
3611 <<
"expected as many dynamic size-producing operations ("
3612 << dynamicSizeProducers.back().size() <<
") as target ops ("
3613 << targets.size() <<
")";
3614 diag.attachNote(transformValue.getLoc()) <<
"for this handle";
3618 for (Operation *op : dynamicSizeProducers.back()) {
3624 DiagnosedSilenceableFailure
diag =
3625 emitSilenceableError() <<
"expected sizes to be produced by ops "
3626 "with a single index-type result";
3627 diag.attachNote(op->
getLoc()) <<
"size producer op";
3628 diag.attachNote(transformValue.getLoc()) <<
"for this handle";
3633 SmallVector<Operation *> tiled;
3634 SmallVector<SmallVector<Operation *, 4>, 4> loops;
3635 loops.resize(getLoops().size());
3636 auto scalableSizes = getScalableSizes();
3637 for (
auto [i, op] : llvm::enumerate(targets)) {
3638 auto tilingInterface = dyn_cast<TilingInterface>(op);
3639 if (!tilingInterface) {
3640 DiagnosedSilenceableFailure
diag =
3641 emitSilenceableError()
3642 <<
"only ops implementing TilingInterface are supported";
3643 diag.attachNote(op->
getLoc()) <<
"target op";
3646 if (tileSizes.size() > tilingInterface.getLoopIteratorTypes().size()) {
3647 DiagnosedSilenceableFailure
diag =
3648 emitSilenceableError()
3649 <<
"too many tiles provided, expected at most "
3650 << tilingInterface.getLoopIteratorTypes().size() <<
" found "
3651 << tileSizes.size();
3652 diag.attachNote(op->
getLoc()) <<
"target op";
3656 scf::SCFTilingOptions tilingOptions;
3657 if (tileSizes.empty()) {
3658 tilingOptions.setTileSizeComputationFunction(
3659 [](OpBuilder &, Operation *) -> SmallVector<OpFoldResult> {
3663 tilingOptions.setTileSizeComputationFunction([&, index = i](OpBuilder &
b,
3665 SmallVector<OpFoldResult> sizes;
3666 sizes.reserve(tileSizes.size());
3667 unsigned dynamicIdx = 0;
3669 for (
auto [ofrIdx, ofr] : llvm::enumerate(
getMixedSizes())) {
3670 if (
auto attr = llvm::dyn_cast_if_present<Attribute>(ofr)) {
3671 if (scalableSizes[ofrIdx]) {
3673 b, getLoc(), cast<IntegerAttr>(attr).getInt());
3675 vector::VectorScaleOp::create(
b, getLoc(),
b.getIndexType());
3677 arith::MulIOp::create(
b, getLoc(), val, vscale).getResult());
3679 sizes.push_back(attr);
3683 ArrayRef<Operation *> dynamicSizes = dynamicSizeProducers[dynamicIdx];
3684 ArrayRef<int64_t> params = paramSizes[dynamicIdx];
3686 assert((dynamicSizes.empty() ^ params.empty()) &&
3687 "expected either dynamic sizes or parameters");
3688 if (!params.empty()) {
3689 sizes.push_back(
b.getIndexAttr(params[index]));
3691 sizes.push_back(dynamicSizes[index]->getResult(0));
3698 tilingOptions.setInterchange(getInterchange());
3699 tilingOptions.setInnerTileAlignments(
3701 FailureOr<scf::SCFTilingResult> maybeTilingResult =
3702 tileUsingSCF(rewriter, tilingInterface, tilingOptions);
3703 if (
failed(maybeTilingResult))
3706 rewriter.
replaceOp(op, maybeTilingResult->replacements);
3708 tiled.append(maybeTilingResult->tiledOps);
3709 for (
const auto &en2 : llvm::enumerate(maybeTilingResult->loops))
3710 loops[en2.index()].push_back(en2.value());
3713 transformResults.
set(cast<OpResult>(getTiledLinalgOp()), tiled);
3714 for (
const auto &en : llvm::enumerate(loops))
3715 transformResults.
set(cast<OpResult>(getLoops()[en.index()]), en.value());
3720SmallVector<OpFoldResult> transform::TileUsingForOp::getMixedSizes() {
3722 ArrayRef<int64_t> tileSizes = getStaticSizes();
3723 SmallVector<OpFoldResult> results;
3724 results.reserve(tileSizes.size());
3725 unsigned dynamicPos = 0;
3727 for (int64_t size : tileSizes) {
3728 if (size == ShapedType::kDynamic) {
3729 results.push_back(dynamic[dynamicPos++]);
3737void transform::TileUsingForOp::getEffects(
3738 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3749void transform::TileUsingForallOp::build(OpBuilder &builder,
3751 ArrayRef<int64_t> staticTileSizes,
3752 transform::TileSizesSpec,
3754 return build(builder,
result,
3762void transform::TileUsingForallOp::build(OpBuilder &builder,
3764 ArrayRef<OpFoldResult> mixedTileSizes,
3765 transform::TileSizesSpec,
3767 SmallVector<int64_t> staticTileSizes;
3768 SmallVector<Value> dynamicTileSizes;
3774 auto operationType = transform::AnyOpType::get(ctx);
3777 TypeRange{operationType, operationType},
3784 staticTileSizesAttr,
3788void transform::TileUsingForallOp::build(OpBuilder &builder,
3790 ArrayRef<int64_t> staticNumThreads,
3791 transform::NumThreadsSpec,
3795 NumThreadsSpec(), mapping);
3798void transform::TileUsingForallOp::build(OpBuilder &builder,
3800 ArrayRef<OpFoldResult> mixedNumThreads,
3801 transform::NumThreadsSpec,
3803 SmallVector<int64_t> staticNumThreads;
3804 SmallVector<Value> dynamicNumThreads;
3811 auto operationType = transform::AnyOpType::get(ctx);
3814 TypeRange{operationType, operationType},
3820 staticNumThreadsAttr,
3827static SmallVector<OpFoldResult>
3833 AffineExpr normalizedUbExpr = (s1 - s0).ceilDiv(s2);
3835 for (
auto [lb,
ub, step] : llvm::zip_equal(lbs, ubs, steps)) {
3837 rewriter, loc, normalizedUbExpr, {lb,
ub, step});
3838 normalizedUbs.push_back(normalizedUb);
3840 return normalizedUbs;
3856 for (
auto [iv, lb, step] : llvm::zip_equal(ivs, lbs, steps)) {
3859 denormalizedIvs.push_back(
3862 return denormalizedIvs;
3873 scf::ForallOp loop) {
3890 auto normalizedForallOp = scf::ForallOp::create(
3891 rewriter, loc, normalizedLbs, normalizedUbs, normalizedSteps,
3892 loop.getOutputs(), loop.getMapping(),
3895 auto normalizedLoopIvs = normalizedForallOp.getInductionVars();
3897 Block *normalizedLoopBlock = normalizedForallOp.getBody();
3902 argValues.append(normalizedForallOp.getRegionIterArgs().begin(),
3903 normalizedForallOp.getRegionIterArgs().end());
3904 Block *origLoopBlock = loop.getBody();
3905 rewriter.
mergeBlocks(origLoopBlock, normalizedLoopBlock, argValues);
3907 rewriter.
replaceOp(loop, normalizedForallOp);
3908 return normalizedForallOp;
3916 scf::SCFTilingResult &tilingResult) {
3918 auto tileableOp = dyn_cast<TilingInterface>(
target);
3921 transformOp.emitSilenceableError()
3922 <<
"only TilingInterface ops are supported";
3923 diag.attachNote(
target->getLoc()) <<
"target op";
3927 scf::SCFTilingOptions
options;
3928 options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
3929 if (!mixedNumThreads.empty()) {
3930 options.setNumThreads(mixedNumThreads);
3932 options.setTileSizes(mixedTileSizes);
3935 options.setMapping(mapping.value().getValue());
3937 FailureOr<scf::SCFTilingResult> maybeTilingResult =
3938 scf::tileUsingSCF(rewriter, tileableOp,
options);
3940 if (failed(maybeTilingResult))
3941 return transformOp.emitDefaultSilenceableFailure(tileableOp);
3943 rewriter.
replaceOp(tileableOp, maybeTilingResult->replacements);
3945 tilingResult = *maybeTilingResult;
3949 if (mixedNumThreads.empty() && !tilingResult.loops.empty()) {
3950 auto generatedForallOp = cast<scf::ForallOp>(tilingResult.loops.front());
3953 scf::ForallOp normalizedForallOp =
3955 tilingResult.loops.front() = normalizedForallOp;
3965 auto transformOp = cast<TransformOpInterface>(getOperation());
3974 getPackedNumThreads()
3976 state, transformOp, mixedNumThreads, getPackedNumThreads())
3978 state, transformOp, mixedNumThreads, getMixedNumThreads());
3982 status = getPackedTileSizes()
3984 state, transformOp, mixedTileSizes, getPackedTileSizes())
3986 state, transformOp, mixedTileSizes, getMixedTileSizes());
3991 scf::SCFTilingResult tilingResult;
3993 rewriter, state, transformOp,
target, mixedNumThreads, mixedTileSizes,
3994 getMapping(), tilingResult);
3995 if (!
diag.succeeded())
3997 if (!tilingResult.loops.empty())
3998 tileOps.push_back(tilingResult.loops.front());
3999 tiledOps.append(tilingResult.tiledOps);
4002 transformResults.
set(cast<OpResult>(getForallOp()), tileOps);
4003 transformResults.
set(cast<OpResult>(getTiledOp()), tiledOps);
4008void transform::TileUsingForallOp::getEffects(
4009 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
4019SmallVector<OpFoldResult> TileUsingForallOp::getMixedNumThreads() {
4024SmallVector<OpFoldResult> TileUsingForallOp::getMixedTileSizes() {
4029LogicalResult TileUsingForallOp::verify() {
4030 int numThreadsSpec =
static_cast<int>(!getMixedNumThreads().empty()) +
4031 static_cast<int>(getPackedNumThreads() != Value());
4032 if (numThreadsSpec > 1)
4034 "num_threads and packed_num_threads are mutually exclusive");
4035 int tileSizesSpec =
static_cast<int>(!getMixedTileSizes().empty()) +
4036 static_cast<int>(getPackedTileSizes() != Value());
4037 if (tileSizesSpec > 1)
4039 "tile_sizes and packed_tile_sizes are mutually exclusive");
4040 if (numThreadsSpec == 0 && tileSizesSpec == 0)
4041 return emitOpError(
"either (packed_)num_threads or (packed_)tile_sizes "
4042 "must be specified");
4050void transform::VectorizeChildrenAndApplyPatternsOp::build(
4051 OpBuilder &builder, OperationState &
result, Value
target,
4052 bool foldTypeExtensionsIntoContract,
bool vectorizePadding,
4053 bool vectorizeExtract,
bool flatten1DDepthwiseConv) {
4055 if (foldTypeExtensionsIntoContract) {
4057 VectorizeChildrenAndApplyPatternsOp::
4058 getFoldTypeExtensionsIntoContractAttrName(
result.name),
4061 if (vectorizePadding) {
4063 VectorizeChildrenAndApplyPatternsOp::getVectorizePaddingAttrName(
4067 if (vectorizeExtract) {
4069 VectorizeChildrenAndApplyPatternsOp::getVectorizeNdExtractAttrName(
4073 if (flatten1DDepthwiseConv) {
4075 VectorizeChildrenAndApplyPatternsOp::getFlatten_1dDepthwiseConvAttrName(
4085struct VectorizationPattern :
public RewritePattern {
4086 explicit VectorizationPattern(MLIRContext *context,
4087 bool vectorizeExtract =
false,
4088 bool flattenConv =
false)
4089 : RewritePattern(MatchAnyOpTypeTag(), 1, context),
4090 vectorizeNDExtract(vectorizeExtract),
4091 flatten1DDepthwiseConv(flattenConv) {}
4092 LogicalResult matchAndRewrite(Operation *op,
4093 PatternRewriter &rewriter)
const override {
4096 "Unsupported Op, cannot vectorize");
4097 FailureOr<VectorizationResult> vectorResults =
4099 {}, vectorizeNDExtract,
4100 flatten1DDepthwiseConv);
4101 if (
failed(vectorResults))
4103 rewriter.
replaceOp(op, vectorResults->replacements);
4110 bool vectorizeNDExtract =
false;
4114 bool flatten1DDepthwiseConv =
false;
4118DiagnosedSilenceableFailure
4119transform::VectorizeChildrenAndApplyPatternsOp::applyToOne(
4120 transform::TransformRewriter &rewriter, Operation *
target,
4121 transform::ApplyToEachResultList &results,
4122 transform::TransformState &state) {
4123 if (!
target->hasTrait<OpTrait::IsIsolatedFromAbove>()) {
4124 auto diag = this->
emitOpError(
"requires isolated-from-above targets");
4125 diag.attachNote(
target->getLoc()) <<
"non-isolated target";
4130 RewritePatternSet patterns(ctx);
4131 patterns.
add<VectorizationPattern>(ctx, getVectorizeNdExtract(),
4132 getFlatten_1dDepthwiseConv());
4134 if (!getDisableTransferPermutationMapLoweringPatterns())
4137 if (!getDisableMultiReductionToContractPatterns())
4142 patterns.
add<linalg::LinalgCopyVTRForwardingPattern,
4143 linalg::LinalgCopyVTWForwardingPattern>(ctx,
4145 vector::TransferReadOp::getCanonicalizationPatterns(patterns, ctx);
4146 vector::TransferWriteOp::getCanonicalizationPatterns(patterns, ctx);
4149 patterns.
add<CopyVectorizationPattern>(ctx);
4151 if (getFoldTypeExtensionsIntoContract())
4154 if (getVectorizePadding()) {
4162 TrackingListener listener(state, *
this);
4165 GreedyRewriteConfig().setListener(&listener))))
4166 return emitDefaultDefiniteFailure(
target);
4176DiagnosedSilenceableFailure transform::VectorizeOp::apply(
4177 transform::TransformRewriter &rewriter,
4178 mlir::transform::TransformResults &transformResults,
4179 mlir::transform::TransformState &state) {
4181 if (std::empty(targets))
4183 auto transformOp = cast<TransformOpInterface>(getOperation());
4184 SmallVector<int64_t> vectorSizes;
4186 state, transformOp, getMixedVectorSizes(), vectorSizes);
4191 for (Operation *
target : targets) {
4194 <<
"Unsupported Op, cannot vectorize";
4196 FailureOr<VectorizationResult> vectorResults =
4198 getVectorizeNdExtract().value_or(
false),
4200 getAssumeDynamicDimsMatchVecSizes().value_or(
false),
4201 getCreateNamedContraction().value_or(
false));
4202 if (
failed(vectorResults)) {
4204 <<
"Attempted to vectorize, but failed";
4212void transform::VectorizeOp::getEffects(
4213 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
4219SmallVector<OpFoldResult> VectorizeOp::getMixedVectorSizes() {
4224LogicalResult transform::VectorizeOp::verify() {
4225 if (getStaticVectorSizes().size() != getScalableSizes().size())
4226 return emitOpError(
"expected same number of vector sizes (")
4227 << getStaticVectorSizes().size() <<
") and scalable sizes ("
4228 << getScalableSizes().size() <<
")";
4236DiagnosedSilenceableFailure
4237transform::HoistRedundantVectorTransfersOp::applyToOne(
4238 transform::TransformRewriter &rewriter, func::FuncOp
target,
4239 transform::ApplyToEachResultList &results,
4240 transform::TransformState &state) {
4253DiagnosedSilenceableFailure
4254transform::HoistRedundantVectorBroadcastsOp::applyToOne(
4255 transform::TransformRewriter &rewriter, mlir::Operation *
target,
4256 transform::ApplyToEachResultList &results,
4257 transform::TransformState &state) {
4268DiagnosedSilenceableFailure transform::ConvertConv2DToImg2ColOp::applyToOne(
4269 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4270 transform::ApplyToEachResultList &results,
4271 transform::TransformState &state) {
4273 auto maybeTransformed =
4276 .Case([&](linalg::Conv2DNhwcHwcfOp op) {
4279 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4282 .Case([&](linalg::DepthwiseConv2DNhwcHwcOp op) {
4285 .Case([&](linalg::Conv2DNchwFchwOp op) {
4288 .Default([&](Operation *op) {
4291 if (
failed(maybeTransformed))
4292 return emitDefaultSilenceableFailure(
target);
4294 results.
push_back(maybeTransformed->first);
4296 results.
push_back(maybeTransformed->second);
4304DiagnosedSilenceableFailure transform::FlattenElementwiseLinalgOp::applyToOne(
4305 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4306 transform::ApplyToEachResultList &results,
4307 transform::TransformState &state) {
4311 <<
"only elementwise flattening is supported";
4313 if (!llvm::all_of(
target.getIndexingMapsArray(), [](AffineMap m) {
4314 return m.isProjectedPermutation(false);
4318 <<
"operators with broadcasting semantics are not supported";
4322 if (
target.getNumLoops() <= 1) {
4329 std::iota(reassociation.begin(), reassociation.end(), 0);
4330 auto maybeFlattened =
4332 if (
failed(maybeFlattened))
4334 <<
"attempted to flatten, but failed";
4335 results.
push_back(maybeFlattened->collapsedOp);
4344DiagnosedSilenceableFailure transform::TransposeConv2DOp::applyToOne(
4345 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4346 transform::ApplyToEachResultList &results,
4347 transform::TransformState &state) {
4349 auto maybeTransformed =
4351 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4354 .Case([&](linalg::Conv2DNhwcFhwcQOp op) {
4357 .Default([&](Operation *op) {
4360 if (
failed(maybeTransformed))
4361 return emitDefaultSilenceableFailure(
target);
4371DiagnosedSilenceableFailure transform::TransposeMatmulOp::applyToOne(
4372 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4373 transform::ApplyToEachResultList &results,
4374 transform::TransformState &state) {
4376 bool transposeLHS = getInputToTranspose() == TransposeMatmulInput::lhs;
4377 auto maybeTransformed =
4379 .Case([&](linalg::MatmulOp op) {
4382 .Case([&](linalg::BatchMatmulOp op) {
4385 .Default(failure());
4386 if (
failed(maybeTransformed))
4396template <
typename OpTy>
4397static DiagnosedSilenceableFailure
4401 static_assert(llvm::is_one_of<OpTy, tensor::InsertSliceOp,
4402 tensor::ParallelInsertSliceOp>() &&
4405 if (
auto copySource =
4406 target.getSource().template getDefiningOp<linalg::CopyOp>()) {
4414 if (isa<mlir::ParallelCombiningOpInterface>(
target.getOperation()))
4417 Value extracted = tensor::ExtractSliceOp::create(
4420 Value copied = linalg::CopyOp::create(rewriter,
target.getLoc(),
4421 target.getSource(), extracted)
4433DiagnosedSilenceableFailure transform::InsertSliceToCopyOp::applyToOne(
4434 transform::TransformRewriter &rewriter, Operation *targetOp,
4435 transform::ApplyToEachResultList &results,
4436 transform::TransformState &state) {
4439 if (
auto target = dyn_cast<tensor::InsertSliceOp>(targetOp))
4440 return doit(rewriter,
target, results, state);
4441 if (
auto target = dyn_cast<tensor::ParallelInsertSliceOp>(targetOp))
4442 return doit(rewriter,
target, results, state);
4444 DiagnosedSilenceableFailure
diag =
4445 emitSilenceableError()
4446 <<
"only InsertSliceOp and ParallelInsertSliceOp ops are supported";
4447 diag.attachNote(targetOp->
getLoc()) <<
"target op";
4455DiagnosedSilenceableFailure transform::MapCopyToThreadsOp::applyToOne(
4456 transform::TransformRewriter &rewriter, Operation *
target,
4457 transform::ApplyToEachResultList &results,
4458 transform::TransformState &state) {
4460 if (!isa<linalg::CopyOp, tensor::PadOp>(
target)) {
4461 DiagnosedSilenceableFailure
diag =
4462 emitSilenceableError()
4463 <<
"only linalg.copy and tensor.pad target ops are supported";
4464 diag.attachNote(
target->getLoc()) <<
"target op";
4467 assert(
target->getNumResults() == 1 &&
"expected single result");
4468 auto resultShapedType = cast<ShapedType>(
target->getResult(0).getType());
4469 if (!resultShapedType.hasStaticShape()) {
4470 DiagnosedSilenceableFailure
diag =
4471 emitSilenceableError()
4472 <<
"only statically sized ops of rank <= 3 are supported";
4473 diag.attachNote(
target->getLoc()) <<
"target op";
4478 int64_t desiredBitAlignment = getDesiredBitAlignment();
4479 int64_t eltBitwidth =
4480 resultShapedType.getElementType().getIntOrFloatBitWidth();
4481 if (desiredBitAlignment % eltBitwidth != 0) {
4482 desiredBitAlignment = eltBitwidth;
4485 gpu::CopyMappingInfo mapping(
4487 getTotalNumThreads(),
4488 desiredBitAlignment,
4489 resultShapedType.getShape(),
4492 resultShapedType.getElementType().getIntOrFloatBitWidth());
4493 if (mapping.status == gpu::CopyMappingInfo::Status::Invalid) {
4494 DiagnosedSilenceableFailure
diag =
4495 emitSilenceableError()
4496 <<
"too few threads to map copy op to threads on the most minor "
4497 "dimension, given alignment and vector size constraints, try "
4498 "smaller tile size of mapping to more threads";
4499 diag.attachNote(
target->getLoc()) <<
"target op";
4505 scf::SCFTilingResult tilingResult;
4512 ArrayRef<OpFoldResult>{},
4513 b.getArrayAttr(mapping.threadMapping),
4515 if (!
diag.succeeded())
4518 results.
push_back(tilingResult.loops.front());
4519 for (
auto *op : tilingResult.tiledOps)
4528DiagnosedSilenceableFailure transform::WinogradConv2DOp::applyToOne(
4529 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4530 transform::ApplyToEachResultList &results,
4531 transform::TransformState &state) {
4533 FailureOr<Operation *> maybeTransformed = failure();
4535 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4540 .Default([&](Operation *op) {
return false; });
4543 return emitSilenceableError()
4544 <<
"this operation is not supported to convert to Winograd Conv2D";
4547 if (
failed(maybeTransformed)) {
4548 return emitSilenceableError() <<
"apply Winograd Conv2D failed";
4555DiagnosedSilenceableFailure transform::DecomposeWinogradOp::applyToOne(
4556 transform::TransformRewriter &rewriter, Operation *
target,
4557 transform::ApplyToEachResultList &results,
4558 transform::TransformState &state) {
4560 FailureOr<Operation *> maybeTransformed = failure();
4563 .Case([&](linalg::WinogradFilterTransformOp op) {
4567 .Case([&](linalg::WinogradInputTransformOp op) {
4571 .Case([&](linalg::WinogradOutputTransformOp op) {
4578 DiagnosedSilenceableFailure
diag =
4579 emitSilenceableError()
4580 <<
"this operation is not supported to decompose into other operations";
4581 diag.attachNote(
target->getLoc()) <<
"target op";
4585 if (
failed(maybeTransformed)) {
4586 DiagnosedSilenceableFailure
diag =
4587 emitSilenceableError() <<
"decompose Winograd operations failed";
4588 diag.attachNote(
target->getLoc()) <<
"target op";
4596#include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOpsEnums.cpp.inc"
4598#define GET_OP_CLASSES
4599#include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp.inc"
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static std::string diag(const llvm::Value &value)
memberIdxs push_back(ArrayAttr::get(parser.getContext(), values))
static llvm::ManagedStatic< PassManagerOptions > options
static void getDynamicSizes(RankedTensorType tp, ValueRange sizes, SmallVectorImpl< Value > &dynSizes)
Collects the dynamic dimension sizes for tp with the assumption that sizes are the dimension sizes fo...
static SmallVector< Value > getTileSizes(Location loc, x86::amx::TileType tType, RewriterBase &rewriter)
Maps the 2-dim vector shape to the two 16-bit tile sizes.
Base type for affine expression.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
Attributes are known-constant values of operations.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getIntegerAttr(Type type, int64_t value)
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
AffineExpr getAffineSymbolExpr(unsigned position)
IntegerAttr getI64IntegerAttr(int64_t value)
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
MLIRContext * getContext() const
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
ArrayAttr getStrArrayAttr(ArrayRef< StringRef > values)
Diagnostic & attachNote(std::optional< Location > loc=std::nullopt)
Attaches a note to the error.
The result of a transform IR operation application.
static DiagnosedSilenceableFailure success()
Constructs a DiagnosedSilenceableFailure in the success state.
bool isDefiniteFailure() const
Returns true if this is a definite failure.
static DiagnosedSilenceableFailure silenceableFailure(Diagnostic &&diag)
Constructs a DiagnosedSilenceableFailure in the silenceable failure state, ready to emit the given di...
bool succeeded() const
Returns true if this is a success.
static DiagnosedSilenceableFailure definiteFailure()
Constructs a DiagnosedSilenceableFailure in the failure state.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
A class for computing basic dominance information.
bool dominates(Operation *a, Operation *b) const
Return true if operation A dominates operation B, i.e.
This is a utility class for mapping one set of IR entities to another.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
IRValueT get() const
Return the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
NamedAttribute represents a combination of a name and an Attribute value.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
virtual OptionalParseResult parseOptionalOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single operand if present.
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
void printFunctionalType(Operation *op)
Print the complete type of an operation in functional form.
bool isSet() const
Returns true if this insert point is set.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setListener(Listener *newListener)
Sets the listener of this builder to the one provided.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Listener * getListener() const
Returns the current listener of this builder, or nullptr if this builder doesn't have a listener.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents a single result from folding an operation.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
This is a value defined by a result of an operation.
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
OpResult getOpResult(unsigned idx)
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
void setOperand(unsigned idx, Value value)
bool hasAttr(StringAttr name)
Return true if the operation has an attribute with the provided name, false otherwise.
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
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.
MLIRContext * getContext()
Return the context this operation is associated with.
unsigned getNumResults()
Return the number of results held by this operation.
bool has_value() const
Returns true if we contain a valid ParseResult value.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
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< LinalgOp > specializeGenericOp(RewriterBase &rewriter, GenericOp genericOp, const GenericOpSpecializationOptions &options={})
Replace the given GenericOp with a namedOp or categoryOp.
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...
LogicalResult linalgOpAnchoredEmptyTensorEliminationStep(RewriterBase &rewriter, Operation *op, bufferization::OneShotAnalysisState &state)
Try to eliminate tensor::EmptyOps inside op that are anchored on a LinalgOp.
FailureOr< GenericOp > generalizeNamedOp(RewriterBase &rewriter, LinalgOp linalgOp)
Create a GenericOp from the given named operation linalgOp and replace the given linalgOp.
FailureOr< Operation * > transposeBatchMatmul(RewriterBase &rewriter, linalg::BatchMatmulOp op, bool transposeLHS=true)
Pattern to replace.
LogicalResult promoteSubviewsPrecondition(Operation *op, LinalgPromotionOptions options)
Promote memref.subviews feeding linalg-on-buffers operations.
LogicalResult copyToGPUPrivateMemory(OpBuilder &b, Value src, Value dst)
Normal copy to between src and dst.
bool isElementwise(LinalgOp op)
Check if a LinalgOp is an element-wise operation.
FailureOr< GenericOp > interchangeGenericOp(RewriterBase &rewriter, GenericOp genericOp, ArrayRef< unsigned > interchangeVector)
Interchange the iterator_types and iterator_maps dimensions and adapts the index accesses of op.
FailureOr< StaticMultiSizeSpecification > computeStaticMultiTileSizes(LinalgOp op, unsigned dimension, int64_t targetSize, int64_t divisor)
void populateDecomposePackUnpackPatterns(RewritePatternSet &patterns)
Populates patterns to decompose linalg.pack and linalg.unpack Ops into e.g.
FailureOr< ContinuousTileSizeSpecification > computeContinuousTileSizes(OpBuilder &builder, TilingInterface op, unsigned dimension, OpFoldResult targetSize, bool emitAssertions)
FailureOr< StaticContinuousTileSizeSpecification > computeStaticContinuousTileSizes(LinalgOp op, unsigned dimension, unsigned targetSize)
FailureOr< SplitReductionResult > splitReduction(RewriterBase &b, LinalgOp op, const ControlSplitReductionFn &controlSplitReductionFn, bool useAlloc=false)
void populateFoldPackUnpackIntoTensorEmptyPatterns(RewritePatternSet &patterns)
Populates patterns with patterns that fold operations like linalg.pack and linalg....
void populateFoldIntoPackAndUnpackPatterns(RewritePatternSet &patterns, const ControlFoldIntoPackUnpackFn &controlFn=nullptr)
Populates patterns with patterns that fold operations like tensor.pad and tensor.extract_slice into t...
void hoistRedundantVectorBroadcasts(RewriterBase &rewriter, Operation *root)
Hoist vector.extract/vector.broadcast pairs out of immediately enclosing scf::ForOp iteratively,...
FailureOr< PackResult > packMatmulGreedily(RewriterBase &rewriter, LinalgOp linalgOp, ArrayRef< OpFoldResult > mnkPackedSizes, ArrayRef< int64_t > mnkPaddedSizesNextMultipleOf, ArrayRef< int64_t > mnkOrder)
Pack a LinalgOp by greedily inferring matmul dimensions (m, n, k) where m and n are proper parallel d...
FailureOr< PackResult > pack(RewriterBase &rewriter, linalg::LinalgOp linalgOp, ArrayRef< OpFoldResult > packedSizes)
Implement packing of a single LinalgOp by packedSizes.
void populateEraseUnnecessaryInputsPatterns(RewritePatternSet &patterns)
Patterns to promote inputs to outputs and remove unused inputs of linalg.generic ops.
std::function< bool(OpOperand *opOperand)> ControlPropagationFn
Function type which is used to control propagation of linalg.pack/unpack ops.
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.