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();
295void transform::ApplySwapExtractSliceWithFillPatternsOp::populatePatterns(
309 SmallVector<Operation *> getNewOps()
const {
310 return SmallVector<Operation *>(newOps.begin(), newOps.end());
314 void notifyOperationInserted(Operation *op,
315 OpBuilder::InsertPoint previous)
override {
316 ForwardingListener::notifyOperationInserted(op, previous);
318 if (previous.
isSet())
322 assert(
inserted.second &&
"expected newly created op");
325 void notifyOperationErased(Operation *op)
override {
326 ForwardingListener::notifyOperationErased(op);
327 op->
walk([&](Operation *op) { newOps.erase(op); });
339 llvm::scope_exit resetListener(
340 [&]() { rewriter.
setListener(previousListener); });
341 NewOpsListener newOpsListener(previousListener);
345 if (getMemcpyOp() ==
"bufferization.materialize_in_destination") {
346 options.memcpyOp = linalg::BufferizeToAllocationOptions::MemcpyOp::
347 MaterializeInDestination;
348 }
else if (getMemcpyOp() ==
"memref.copy") {
351 }
else if (getMemcpyOp() ==
"linalg.copy") {
355 llvm_unreachable(
"invalid memcpy op");
357 if (getAllocOp() ==
"memref.alloc") {
360 }
else if (getAllocOp() ==
"memref.alloca") {
364 llvm_unreachable(
"invalid alloc op");
366 options.bufferizeDestinationOnly = getBufferizeDestinationOnly();
367 options.emitDealloc = getEmitDealloc();
371 getMemorySpace().has_value() ? getMemorySpace().value() :
Attribute();
378 <<
"failed to bufferize operation";
379 diag.attachNote(op->
getLoc()) <<
"target payload op";
382 allocatedBuffers.push_back(buffer);
386 results.
setValues(cast<OpResult>(getAllocatedBuffer()), allocatedBuffers);
387 results.
set(cast<OpResult>(getNewOps()), newOpsListener.getNewOps());
391void transform::BufferizeToAllocationOp::getEffects(
393 if (getBufferizeDestinationOnly()) {
404LogicalResult transform::BufferizeToAllocationOp::verify() {
405 if (getMemcpyOp() !=
"bufferization.materialize_in_destination" &&
406 getMemcpyOp() !=
"memref.copy" && getMemcpyOp() !=
"linalg.copy")
408 if (getAllocOp() !=
"memref.alloc" && getAllocOp() !=
"memref.alloca")
421 auto linalgOp = dyn_cast<linalg::LinalgOp>(operand.
getOwner());
428 Value blockArgument = linalgOp.getMatchingBlockArgument(&operand);
436 if (!isa<TensorType, FloatType, IntegerType>(value.
getType()))
438 return llvm::any_of(value.
getUses(),
448 auto type = dyn_cast<RankedTensorType>(
tensor.getType());
450 return emitSilenceableError() <<
"non-tensor type: " <<
tensor;
464 for (
auto [pos, dim] : llvm::enumerate(type.getShape())) {
465 if (!ShapedType::isDynamic(dim))
470 tensor::DimOp::create(rewriter,
tensor.getLoc(),
tensor, cst);
471 preservedOps.insert(dimOp);
472 dynamicDims.push_back(dimOp);
474 auto allocation = bufferization::AllocTensorOp::create(
475 rewriter,
tensor.getLoc(), type, dynamicDims);
477 if (getMemorySpaceAttr())
478 allocation.setMemorySpaceAttr(getMemorySpaceAttr());
479 Value allocated = allocation;
483 if (needsMaterialization) {
484 auto copy = bufferization::MaterializeInDestinationOp::create(
486 preservedOps.insert(
copy);
487 promoted.push_back(
copy.getResult());
489 promoted.push_back(allocated);
493 results.
setValues(cast<OpResult>(getPromoted()), promoted);
497void transform::PromoteTensorOp::getEffects(
513 FailureOr<linalg::LinalgOp> res =
515 if (succeeded(res)) {
519 return emitDefaultSilenceableFailure(
target);
533 auto decomposableOp = dyn_cast<AggregatedOpInterface>(
target);
534 if (!decomposableOp) {
536 "payload is not a decomposable op"));
537 return emitDefaultSilenceableFailure(
target);
540 FailureOr<SmallVector<Value>> maybeNewResults =
541 decomposableOp.decomposeOperation(rewriter);
542 if (
failed(maybeNewResults))
543 return emitDefaultSilenceableFailure(
target);
545 rewriter.
replaceOp(decomposableOp, *maybeNewResults);
546 for (
Value val : *maybeNewResults) {
547 Operation *definition = val.getDefiningOp();
558void transform::EliminateLinalgOpAnchoredEmptyTensorsOp::getEffects(
565transform::EliminateLinalgOpAnchoredEmptyTensorsOp::apply(
569 options.allowReturnAllocsFromLoops =
true;
575 <<
"failed to analyze op";
577 rewriter,
target, state)))
579 <<
"failed to eliminate LinalgOp anchored tensor.empty ops";
592 bool applyCleanup,
bool useForall) {
594 builder,
result, loopTypes,
600 applyCleanup, useForall);
606 bool applyCleanup,
bool useForall) {
614 applyCleanup, useForall);
621 bool applyCleanup,
bool useForall) {
625 build(builder,
result, loopTypes,
target, mixedTileSizes,
626 mixedTileInterchange, applyCleanup, useForall);
633 bool applyCleanup,
bool useForall) {
640 staticTileInterchange);
645 auto staticTileInterchangeAttr =
647 unsigned numExpectedLoops =
648 useForall ? 1 : staticTileSizes.size() - llvm::count(staticTileSizes, 0);
650 resultTypes.reserve(numExpectedLoops);
651 assert((loopTypes.size() == 1 || loopTypes.size() == numExpectedLoops) &&
652 "expected one loop type or as many as loops");
653 if (loopTypes.size() == 1)
654 resultTypes.append(numExpectedLoops, loopTypes[0]);
656 llvm::append_range(resultTypes, loopTypes);
661 dynamicTileInterchange,
664 staticTileInterchangeAttr,
672template <
typename Range>
677 function_ref<FailureOr<scf::SCFTileAndFuseResult>(TilingInterface)>
681 size_t numTargets = llvm::range_size(payloadOps);
684 auto tilingInterfaceOp = dyn_cast<TilingInterface>(
target);
685 if (!tilingInterfaceOp)
686 return transformOp->
emitError(
"only TilingInterface ops are supported");
689 FailureOr<scf::SCFTileAndFuseResult> tiledResults =
690 applyFn(tilingInterfaceOp);
691 if (failed(tiledResults))
696 llvm::append_range(opsToReplace, tiledResults->fusedProducers);
697 for (
Operation *toReplace : opsToReplace) {
698 for (
OpResult res : toReplace->getResults())
699 if (
auto replacement = tiledResults->replacements.lookup(res))
701 if (toReplace->use_empty()) {
707 tiledLinalgOps.push_back(tiledResults->tiledAndFusedOps.front());
708 assert(tiledResults->loops.size() == numLoops &&
709 "Mismatched number of loops, tile and fuse transform should have "
711 for (
unsigned int i = 0; i < numLoops; ++i)
712 loopOps[i].
push_back(tiledResults->loops[i]);
715 transformResults.
set(transformOp->
getOpResult(0), tiledLinalgOps);
724 for (
unsigned int idx = 0; idx < numTargets; ++idx)
725 for (
unsigned int i = 0; i < numLoops; ++i)
726 flattenedLoopOps.push_back(loopOps[i][idx]);
727 transformResults.
set(transformOp->
getOpResult(1), flattenedLoopOps);
729 for (
unsigned int i = 0; i < numLoops; ++i)
730 transformResults.
set(transformOp->
getOpResult(i + 1), loopOps[i]);
740 auto transformOp = cast<TransformOpInterface>(getOperation());
746 state, transformOp, mixedTileSizes, getPackedTileSizes())
748 state, transformOp, mixedTileSizes, getMixedTileSizes());
753 state, transformOp, getMixedTileInterchange(), tileInterchange);
757 scf::SCFTilingOptions tilingOptions;
758 tilingOptions.interchangeVector = tileInterchange;
759 bool useForall = getUseForall();
760 tilingOptions.setLoopType(useForall
761 ? scf::SCFTilingOptions::LoopType::ForallOp
762 : scf::SCFTilingOptions::LoopType::ForOp);
763 tilingOptions = tilingOptions.setTileSizes(mixedTileSizes);
764 scf::SCFTileAndFuseOptions tileAndFuseOptions;
765 tileAndFuseOptions.tilingOptions = tilingOptions;
768 tileAndFuseOptions.tilingOptions.setInnerTileAlignments(
771 if (getApplyCleanup()) {
774 tensor::ExtractSliceOp::getCanonicalizationPatterns(patterns, context);
777 tileAndFuseOptions.cleanupPatterns = std::move(patterns);
784 numLoops = llvm::count_if(mixedTileSizes, [](
OpFoldResult ofr) {
785 auto attr = dyn_cast<Attribute>(ofr);
788 return cast<IntegerAttr>(attr).getInt() != 0;
792 rewriter, getOperation(), state.
getPayloadOps(getTarget()), numLoops,
793 transformResults, getPackedTileSizes() !=
nullptr,
794 [&](TilingInterface tilingInterfaceOp)
795 -> FailureOr<scf::SCFTileAndFuseResult> {
803LogicalResult transform::FuseOp::verify() {
804 bool hasPackedTiles = getPackedTileSizes() !=
nullptr;
805 if (!getMixedTileSizes().empty() && hasPackedTiles)
807 "tile_sizes and packed_tile_sizes are mutually exclusive");
809 auto iterspace_rank = getStaticTileSizes().size();
811 if (permutation.size() > iterspace_rank)
813 <<
"interchange length exceeds iteration space dimensions ("
814 << iterspace_rank <<
"), found " << getTileInterchange();
816 for (
int64_t v : permutation) {
817 if (!ShapedType::isDynamic(v)) {
818 if (v < 0 || v >=
static_cast<int64_t>(iterspace_rank))
819 return emitOpError() <<
"expects interchange values to be in range [0, "
820 << iterspace_rank <<
"), found: " << v;
822 return emitOpError() <<
"found duplicate interchange value: " << v;
828 size_t numExpectedLoops = getUseForall() || hasPackedTiles
830 : sizes.size() - llvm::count(sizes, 0);
831 if (numExpectedLoops != getNumResults() - 1)
832 return emitOpError() <<
"expects " << numExpectedLoops <<
" loop results";
842 return getMixedValues(getStaticTileInterchange(), getTileInterchange(),
846void transform::FuseOp::getEffects(
860void transform::FuseIntoContainingOp::build(
OpBuilder &builder,
863 Value containingOp) {
864 result.addOperands({producerOp, containingOp});
865 auto resultType = transform::AnyOpType::get(builder.
getContext());
866 result.addTypes({resultType, resultType});
882 (domInfo.
dominates(containingOp, user))) {
883 dominatedUsers.insert(user);
886 if (dominatedUsers.empty())
890 auto forallOp = cast<scf::ForallOp>(containingOp);
896 auto genericOp = dyn_cast<linalg::GenericOp>(producerOp);
901 newOuts.push_back(outputs[resultNumber]);
904 auto newforallOp = scf::ForallOp::create(
905 rewriter, loc, forallOp.getMixedLowerBound(),
906 forallOp.getMixedUpperBound(), forallOp.getMixedStep(), newOuts,
907 forallOp.getMapping());
909 newforallOp.getRegion().takeBody(forallOp.getRegion());
914 newforallOp.getBody()->addArgument(newOuts.back().getType(),
915 newOuts.back().getLoc());
916 auto bbArgs = newforallOp.getBody()->getArguments();
919 Operation *op = use.getOwner();
920 return newforallOp->isProperAncestor(op);
924 scf::InParallelOp terminatorOp = newforallOp.getTerminator();
926 terminatorOp.getYieldingOps(), [](
Operation &op) { return &op; });
927 Operation *firstYieldOp = yieldingOps.front();
930 Value dst = newforallOp.getRegionIterArgs().back();
932 tensor::ParallelInsertSliceOp::create(rewriter, firstYieldOp->
getLoc(), src,
933 dst, offsets, sizes, strides);
935 for (
auto result : llvm::enumerate(forallOp.getResults())) {
937 newforallOp->getResult(
result.index()));
940 newforallOp->getResults().back(),
942 Operation *user = use.getOwner();
943 return dominatedUsers.contains(user);
957 destWorklist.push_back(dst);
959 while (!destWorklist.empty()) {
960 Value currentDst = destWorklist.pop_back_val();
964 if (src == currentDst)
969 auto bbArg = dyn_cast<BlockArgument>(currentDst);
973 Block *parentBlock = bbArg.getOwner();
974 assert(parentBlock &&
"unlinked block argument");
977 assert(parentOp &&
"expected block argument with parent operation");
980 auto parentLoop = dyn_cast<LoopLikeOpInterface>(parentOp);
984 for (
auto innerIterArg : parentLoop.getRegionIterArgs()) {
986 OpOperand *operand = parentLoop.getTiedLoopInit(innerIterArg);
987 Value loopBlockArgument =
989 destWorklist.push_back(loopBlockArgument);
1002static std::tuple<SmallVector<Operation *>,
Operation *>
1006 LDBG() <<
"Try to fuse a direct extract use";
1007 auto tileableProducer = dyn_cast<TilingInterface>(producerOp);
1008 if (!tileableProducer) {
1010 <<
"producer is not a TileableInterface: " << *producerOp;
1017 auto it = llvm::find_if(tileableProducer->getUsers(), [&](
Operation *user) {
1018 auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user);
1019 return sliceOp && containingOp->isProperAncestor(sliceOp);
1023 if (it == tileableProducer->getUsers().end()) {
1024 diag.attachNote(tileableProducer->getLoc())
1025 <<
"could not find fusion opportunity for: " << *tileableProducer;
1028 auto sliceOpToTile = cast<tensor::ExtractSliceOp>(*it);
1041 if (LoopLikeOpInterface containerLoop =
1042 dyn_cast<LoopLikeOpInterface>(sliceOpToTile->getParentOp())) {
1048 auto dpsInterface = dyn_cast<DestinationStyleOpInterface>(
clone);
1052 for (
OpOperand &initOperandPtr : dpsInterface.getDpsInitsMutable()) {
1053 Value producerOperand =
1054 clone->getOperand(initOperandPtr.getOperandNumber());
1056 containerLoop.getRegionIterArgs()) {
1057 OpOperand *bbArg = containerLoop.getTiedLoopInit(containerIterArg);
1058 Value consumerOperand =
1062 initOperandPtr.set(containerIterArg);
1068 tileableProducer = dyn_cast<TilingInterface>(
clone);
1073 cast<OpResult>(sliceOpToTile.getSource()).getResultNumber();
1074 LDBG() <<
"resultNumber: " << resultNumber;
1079 FailureOr<TilingResult> tileAndFuseResult =
1080 tileableProducer.generateResultTileValue(rewriter, resultNumber, offsets,
1081 sizes, innerTileAlignments);
1083 if (failed(tileAndFuseResult)) {
1084 diag.attachNote(tileableProducer->getLoc())
1085 <<
"failed to tile producer op: " << *tileableProducer;
1090 for (
auto *tiledOp : tileAndFuseResult->tiledOps) {
1091 LDBG() <<
"tiledProducer: " << *tiledOp;
1096 auto maybeRankReduced = tensor::ExtractSliceOp::rankReduceIfNeeded(
1097 rewriter, sliceOpToTile->getLoc(), tileAndFuseResult->tiledValues[0],
1098 cast<RankedTensorType>(sliceOpToTile->getResult(0).getType()).getShape());
1099 if (failed(maybeRankReduced)) {
1101 <<
"shape types don't match (missing canonicalization?):\nTiledOp: "
1102 << tileAndFuseResult->tiledValues[0]
1103 <<
"\nSliceOp: " << sliceOpToTile.getOperation() <<
'\n';
1106 rewriter.
replaceOp(sliceOpToTile, *maybeRankReduced);
1110 rewriter,
diag, producerOp, containingOp, *tileAndFuseResult,
1111 resultNumber, offsets, sizes);
1114 if (isa<LoopLikeOpInterface>(containingOp))
1115 rewriter.
eraseOp(tileableProducer);
1117 return std::make_tuple(tileAndFuseResult->tiledOps, newContainingOp);
1130 LDBG() <<
"Try to fuse an extract use through block argument";
1132 auto tileableProducer = dyn_cast<TilingInterface>(producerOp);
1133 if (!tileableProducer) {
1135 <<
"producer is not a TileableInterface: " << *producerOp;
1140 scf::ForallOp forallOp;
1141 auto itProducerUses =
1142 llvm::find_if(tileableProducer->getUses(), [&](
OpOperand &use) {
1143 forallOp = dyn_cast<scf::ForallOp>(use.getOwner());
1147 if (!forallOp || forallOp != containingOp) {
1148 diag.attachNote(tileableProducer->getLoc())
1149 <<
"could not find a use by the containing op: " << *tileableProducer;
1164 auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user);
1165 return sliceOp && containingOp->isProperAncestor(sliceOp);
1169 if (itBBArgUsers == bbArg.
getUsers().end()) {
1171 <<
"could not find fusion opportunity for bbArg: " << bbArg;
1174 auto sliceOpToTile = cast<tensor::ExtractSliceOp>(*itBBArgUsers);
1182 int64_t resultNumber = cast<OpResult>(pUse->
get()).getResultNumber();
1183 LDBG() <<
"resultNumber: " << resultNumber;
1188 rewriter, tileableProducer->getLoc(), tileableProducer,
1189 destinationTensors))) {
1190 diag.attachNote(tileableProducer->getLoc())
1191 <<
"failed to get destination tensors for: " << *tileableProducer;
1196 bvm.
map(destinationTensors[resultNumber], bbArg);
1197 auto tileableProducerClone =
1198 cast<TilingInterface>(rewriter.
clone(*tileableProducer, bvm));
1199 llvm::scope_exit scopeGuard(
1200 [&]() { rewriter.
eraseOp(tileableProducerClone); });
1203 FailureOr<TilingResult> tileAndFuseResult =
1204 tileableProducerClone.generateResultTileValue(
1205 rewriter, resultNumber, sliceOpToTile.getMixedOffsets(),
1206 sliceOpToTile.getMixedSizes(), innerTileAlignments);
1207 if (failed(tileAndFuseResult)) {
1208 diag.attachNote(tileableProducer->getLoc())
1209 <<
"failed to tile producer op: " << *tileableProducer;
1214 auto maybeRankReduced = tensor::ExtractSliceOp::rankReduceIfNeeded(
1215 rewriter, sliceOpToTile->getLoc(), tileAndFuseResult->tiledValues[0],
1216 cast<RankedTensorType>(sliceOpToTile->getResult(0).getType()).getShape());
1217 assert(succeeded(maybeRankReduced) &&
"unexpected shape");
1218 rewriter.
replaceOp(sliceOpToTile, *maybeRankReduced);
1223 destinationTensors.front());
1226 return tileAndFuseResult->tiledOps;
1232 LDBG() <<
"Try to fuse an use by cloning";
1239 uses.push_back(&use);
1244 if (containingOp == use.getOwner()) {
1246 <<
"producer op use by containing op cannot be fused by cloning";
1254 diag.attachNote(producerOp->
getLoc()) <<
"no fusion opportunity by cloning";
1263 assert(!isa<tensor::ParallelInsertSliceOp>(use->
getOwner()) &&
1264 "Parallel insert slice is not a valid clone destination");
1265 unsigned resultNumber = cast<OpResult>(use->
get()).getResultNumber();
1266 LDBG() <<
"resultNumber: " << resultNumber;
1270 fusedOp = rewriter.
clone(*producerOp);
1272 use->
getOwner(), [&] { use->set(fusedOp->getOpResult(resultNumber)); });
1277bool transform::FuseIntoContainingOp::allowsRepeatedHandleOperands() {
1282LogicalResult transform::FuseIntoContainingOp::verify() {
1292 auto containingOps = state.
getPayloadOps(getContainingOp());
1293 if (!llvm::hasSingleElement(containingOps)) {
1295 <<
"requires exactly one containing_op handle (got "
1296 << llvm::range_size(containingOps) <<
")";
1298 Operation *containingOp = *containingOps.begin();
1307 if (std::empty(producerOps)) {
1309 results.
set(cast<OpResult>(getNewContainingOp()), {containingOp});
1316 auto getNextProducer = [&]() -> FailureOr<Operation *> {
1317 for (
const auto &it :
enumerate(remainingProducers)) {
1320 int64_t numUsesInContainingOp =
1322 return containingOp->isAncestor(op);
1327 if (numUsesInContainingOp > 0) {
1328 if (numUsesInContainingOp == 1)
1329 remainingProducers.erase(remainingProducers.begin() + it.index());
1336 while (!remainingProducers.empty()) {
1337 auto nextProducer = getNextProducer();
1338 if (
failed(nextProducer)) {
1340 <<
"could not find next producer to fuse into container";
1341 diag.attachNote(containingOp->
getLoc()) <<
"containing op";
1349 diag <<
"could not fuse " << *producerOp <<
" into " << *containingOp;
1357 rewriter,
diag, producerOp, containingOp, innerTileAlignments);
1358 if (!tiledOps.empty()) {
1359 LDBG() <<
"\nFused a direct extract use\n" << *containingOp;
1360 fusedOps.append(tiledOps);
1361 if (newContainingOp) {
1369 LogicalResult replacementStatus =
1372 (
void)replacementStatus;
1373 assert(succeeded(replacementStatus) &&
1374 "unable to update transform state mapping");
1375 rewriter.
eraseOp(containingOp);
1376 containingOp = newContainingOp;
1383 rewriter,
diag, producerOp, containingOp, innerTileAlignments);
1384 if (!tiledContainingOpOperand.empty()) {
1385 LDBG() <<
"\nFused an extract use through block argument\n"
1387 fusedOps.append(tiledContainingOpOperand);
1394 LDBG() <<
"\nFused an use by cloning\n" << *containingOp;
1395 fusedOps.push_back(cloned);
1401 results.
set(cast<OpResult>(getFusedOp()), fusedOps);
1402 results.
set(cast<OpResult>(getNewContainingOp()), {containingOp});
1406void transform::FuseIntoContainingOp::getEffects(
1424 if (isa<GenericOp>(
target)) {
1430 if (succeeded(generic)) {
1431 results.
push_back(generic->getOperation());
1434 return emitDefaultSilenceableFailure(
target);
1447 if (!isa<GenericOp>(
target)) {
1454 FailureOr<LinalgOp> named =
1456 if (succeeded(named)) {
1457 results.
push_back(named->getOperation());
1460 return emitDefaultSilenceableFailure(
target);
1474 if (interchangeVector.empty()) {
1479 unsigned numLoops = cast<LinalgOp>(
target.getOperation()).getNumLoops();
1480 if (interchangeVector.size() != numLoops) {
1481 return emitSilenceableError()
1482 << getIteratorInterchangeAttrName() <<
" has length ("
1483 << interchangeVector.size()
1484 <<
") different from the number of loops in the target operation ("
1495LogicalResult transform::InterchangeOp::verify() {
1497 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, permutation.size()));
1498 if (!std::is_permutation(sequence.begin(), sequence.end(),
1499 permutation.begin(), permutation.end())) {
1501 <<
"expects iterator_interchange to be a permutation, found "
1502 << getIteratorInterchange();
1517 if (!isa<linalg::CopyOp>(targetOp)) {
1519 emitSilenceableError() <<
"only linalg.copy target ops are supported";
1520 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1524 auto copyOp = dyn_cast<linalg::CopyOp>(targetOp);
1525 if (!copyOp.hasPureBufferSemantics()) {
1527 emitSilenceableError()
1528 <<
"cannot transform a linalg.copy on tensors into a memref.copy";
1529 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1535 assert(inputs.size() == 1 &&
"expected linalg copy op with one input");
1536 assert(outputs.size() == 1 &&
"expected memref copy op with one output");
1537 Value input = inputs.front();
1538 Value output = outputs.front();
1543 if (!isa<ShapedType>(input.
getType())) {
1545 emitSilenceableError()
1546 <<
"cannot transform a linalg.copy which input has no shape";
1547 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1552 assert(isa<ShapedType>(output.
getType()));
1554 if (cast<ShapedType>(input.
getType()).getElementType() !=
1555 cast<ShapedType>(output.
getType()).getElementType()) {
1557 emitSilenceableError()
1558 <<
"cannot transform a linalg.copy with different source and "
1559 "destination element types ";
1560 diag.attachNote(targetOp->
getLoc()) <<
"target op";
1581 bool lowerPadLikeWithInsertSlice = getLowerPadLikeWithInsertSlice();
1582 FailureOr<LowerPackResult> res =
1586 <<
"cannot lower to pad + expand + transpose";
1589 transformResults.
push_back(res->expandShapeOp);
1590 transformResults.
push_back(res->transposeOp);
1603 bool lowerUnpadLikeWithExtractSlice = getLowerUnpadLikeWithExtractSlice();
1604 FailureOr<LowerUnPackOpResult> res =
1608 emitSilenceableError()
1609 <<
"cannot lower to transpose + collapse + extract";
1610 diag.attachNote(
target->getLoc()) <<
"target payload op";
1613 transformResults.
push_back(res->emptyOp);
1614 transformResults.
push_back(res->transposeOp);
1615 transformResults.
push_back(res->collapseShapeOp);
1616 transformResults.
push_back(res->extractSliceOp);
1617 transformResults.
push_back(res->copyOp);
1628 result.addAttribute(MatchOp::getOpsAttrName(
result.name),
1637 result.addAttribute(MatchOp::getOpsAttrName(
result.name),
1639 result.addTypes(resultTypes);
1647 if (getOps().has_value())
1648 strs.insert_range(getOps()->getAsValueRange<StringAttr>());
1651 if (!llvm::hasSingleElement(payloadOps)) {
1656 bool incorrectNumOperandTypes =
false;
1663 if (getInterface().has_value()) {
1664 auto iface = getInterface().value();
1665 if (iface == transform::MatchInterfaceEnum::LinalgOp &&
1668 if (iface == transform::MatchInterfaceEnum::TilingInterface &&
1669 !isa<TilingInterface>(op))
1671 if (iface == transform::MatchInterfaceEnum::LoopLikeInterface &&
1672 !isa<LoopLikeOpInterface>(op))
1677 if (getOpAttrs().has_value()) {
1678 DictionaryAttr opAttrs = getOpAttrs().value();
1680 if (attr.getName() == getInterfaceAttrName() ||
1681 attr.getName() == getOpsAttrName())
1683 std::optional<Attribute> inherent = op->
getInherentAttr(attr.getName());
1689 if (actual != attr.getValue())
1694 if (getFilterResultType().has_value()) {
1695 Type t = getFilterResultType().value();
1700 if (getFilterOperandTypes().has_value()) {
1701 mlir::ArrayAttr types = getFilterOperandTypes().value();
1704 if (types.size() == 1) {
1707 dyn_cast<mlir::TypeAttr>(getFilterOperandTypes().value()[0]);
1708 Type t = cast<::mlir::Type>(typeattr.getValue());
1710 [&](
Type operandType) { return operandType == t; }))
1715 if (types.size() != operandTypes.size()) {
1716 incorrectNumOperandTypes =
true;
1720 for (
auto [attr, operandType] :
1721 llvm::zip_equal(getFilterOperandTypes().value(), operandTypes)) {
1722 auto typeattr = cast<mlir::TypeAttr>(attr);
1723 Type type = cast<::mlir::Type>(typeattr.getValue());
1725 if (type != operandType)
1736 (*payloadOps.begin())->walk(matchFun);
1737 if (incorrectNumOperandTypes)
1739 "type, then it must contain as much types as "
1740 "the number of operands in the target ops");
1741 results.
set(cast<OpResult>(getResult()), res);
1756 Type &targetType,
Type &lowSizeType,
1758 Type &splitPointType) {
1759 FunctionType funcType;
1761 if (failed(parser.
parseType<FunctionType>(funcType)))
1764 if (funcType.getNumInputs() != 1 || funcType.getNumResults() != 1) {
1765 parser.
emitError(typeLoc) <<
"expects a trailing functional type with one "
1766 "argument and one result";
1768 targetType = funcType.getInput(0);
1769 lowSizeType = highSizeType = splitPointType = funcType.getResult(0);
1777 if (isa<TransformParamTypeInterface>(getLowSize().
getType())) {
1778 if (
target.hasDynamicShape()) {
1779 auto diag = emitSilenceableError()
1780 <<
"cannot compute parametric tile sizes for dynamically "
1781 "shaped payload op";
1782 diag.attachNote(
target->getLoc()) <<
"payload op";
1787 target, getDimension(), getTargetSize(), getDivisor());
1789 return emitSilenceableError()
1790 <<
"failed to compute multi-size tiling sizes";
1794 results.
assign(llvm::map_range(
1796 spec->lowTileSize * spec->lowTripCount}),
1797 [&builder,
this](
int64_t value) {
1809 builder,
target, getDimension(), targetSize, divisor);
1811 return emitSilenceableError() <<
"could not generate tile size computation";
1818 {spec->lowTileSize, spec->lowTripCount});
1819 Operation *lowTileSize = spec->lowTileSize.getDefiningOp();
1820 Operation *highTileSize = spec->highTileSize.getDefiningOp();
1821 assert(lowTileSize && highTileSize && splitPoint &&
1822 "tile sizes are not produced by operations");
1830void transform::MultiTileSizesOp::getEffects(
1834 if (isa<TransformParamTypeInterface>(getLowSize().
getType()))
1840LogicalResult transform::MultiTileSizesOp::verify() {
1843 return emitOpError() <<
"expects all results type to be the same";
1862 Type linalgOpHType = transform::OperationType::get(
1863 builder.
getContext(), GenericOp::getOperationName());
1882 if (std::empty(targetOps)) {
1883 transformResults.
set(cast<OpResult>(getPackedOp()),
1888 auto linalgOp = dyn_cast<LinalgOp>(*targetOps.begin());
1889 if (!llvm::hasSingleElement(targetOps) || !linalgOp) {
1890 return emitSilenceableError()
1891 <<
"requires target to map to exactly 1 LinalgOp (got "
1892 << llvm::range_size(targetOps) <<
")";
1895 if (getMixedPackedSizes().size() != linalgOp.getNumLoops()) {
1896 return emitSilenceableError()
1897 <<
"requires number of packed sizes match the number of loops ("
1898 << getMixedPackedSizes().size() <<
" vs " << linalgOp.getNumLoops()
1905 state, *
this, packedSizes, getMixedPackedSizes());
1908 FailureOr<PackResult> maybeResult =
pack(rewriter, linalgOp, packedSizes);
1912 transformResults.
set(cast<OpResult>(getPackedOp()),
1913 {maybeResult->packedLinalgOp.getOperation()});
1917void transform::PackOp::getEffects(
1929LogicalResult transform::PackGreedilyOp::verify() {
1931 return emitOpError() << getMatmulInnerDimsOrderAttrName()
1932 <<
" is not a valid permutation";
1935 if (!getMatmulPaddedSizesNextMultipleOf().empty()) {
1936 for (
auto [s, nmo] :
1937 llvm::zip_equal(getMixedMatmulPackedSizes(),
1938 getMatmulPaddedSizesNextMultipleOf())) {
1941 (!maybeStaticPackedSize.has_value() || *maybeStaticPackedSize != 0)) {
1942 return emitOpError() <<
"at most one of the packed_size and the "
1943 "padded_sizes_next_multiple_of can be nonzero "
1944 "for the matmul strategy";
1957 auto linalgOp = dyn_cast<LinalgOp>(op);
1968 getMixedMatmulPackedSizes(),
1970 getMatmulPaddedSizesNextMultipleOf(),
1971 getMatmulInnerDimsOrder());
1972 if (succeeded(packResult)) {
1973 results.push_back(packResult->packedLinalgOp);
1976 results.push_back(linalgOp);
1978 transformResults.
set(cast<OpResult>(getPackedOp()), results);
1984 return getMixedValues(getStaticMatmulPackedSizes(), getMatmulPackedSizes(),
1988void transform::PackGreedilyOp::getEffects(
2000LogicalResult transform::PackTransposeOp::verify() {
2003 <<
" is not a valid permutation";
2007 <<
" is not a valid permutation";
2009 if (getInnerPerm().empty() && getOuterPerm().empty()) {
2010 return emitOpError() <<
" at least one of " << getInnerPermAttrName()
2011 <<
" or " << getOuterPermAttrName()
2012 <<
" must be specified";
2018enum class OuterOrInnerPerm { Outer = 0, Inner = 1 };
2028template <
typename RelayoutOpTy>
2029static bool isValidPackingPermutation(
2031 OuterOrInnerPerm outerOrInnerPerm = OuterOrInnerPerm::Outer) {
2033 llvm::is_one_of<RelayoutOpTy, linalg::PackOp, linalg::UnPackOp>::value,
2034 "applies to only pack or unpack operations");
2035 if (!op || permutation.empty())
2037 size_t innerRank = op.getInnerDimsPos().size();
2038 if (outerOrInnerPerm == OuterOrInnerPerm::Inner)
2042 if (std::is_same<RelayoutOpTy, linalg::PackOp>::value) {
2043 return permutation.size() == op.getSourceRank() &&
2046 return permutation.size() == op.getDestRank() &&
2054 auto packOrUnpackOps = state.
getPayloadOps(getTargetPackOrUnPackOp());
2057 if (std::empty(packOrUnpackOps)) {
2058 transformResults.
set(cast<OpResult>(getPackedOp()), {});
2059 transformResults.
set(cast<OpResult>(getPackOp()), {});
2060 transformResults.
set(cast<OpResult>(getUnPackOp()), {});
2066 if (!llvm::hasSingleElement(packOrUnpackOps) ||
2067 !llvm::hasSingleElement(linalgOps)) {
2068 return emitSilenceableError()
2069 <<
"requires target to map to exactly 1 "
2070 "packing op and 1 packed op ("
2071 <<
"got " << llvm::range_size(packOrUnpackOps) <<
" and "
2072 << llvm::range_size(linalgOps) <<
")";
2076 auto packOp = dyn_cast<linalg::PackOp>(*packOrUnpackOps.begin());
2077 auto unPackOp = dyn_cast<linalg::UnPackOp>(*packOrUnpackOps.begin());
2078 if ((!packOp && !unPackOp)) {
2079 return emitSilenceableError() <<
"requires target to map to a "
2080 "linalg.pack or linalg.unpack";
2082 LinalgOp linalgOpTarget = dyn_cast<LinalgOp>(*linalgOps.begin());
2083 if (!linalgOpTarget)
2084 return emitSilenceableError() <<
"requires a LinalgOp target";
2088 if (packOp && packOp.getResult().hasOneUse())
2089 linalgOp = dyn_cast<LinalgOp>(*(packOp.getResult().getUsers().begin()));
2091 linalgOp = unPackOp.getSource().getDefiningOp<LinalgOp>();
2092 if (linalgOp != linalgOpTarget) {
2094 packOp ? StringLiteral{
"not a single use by the LinalgOp target"}
2095 : StringLiteral{
"not produced by the LinalgOp target"};
2096 return emitSilenceableError() << errorMsg;
2102 assert(!packOp &&
"packOp must be null on entry when unPackOp is not null");
2103 OpOperand *packUse = linalgOp.getDpsInitOperand(
2104 cast<OpResult>(unPackOp.getSource()).getResultNumber());
2106 if (!packOp || !packOp.getResult().hasOneUse())
2107 return emitSilenceableError() <<
"could not find matching pack op";
2111 for (
auto permType : {OuterOrInnerPerm::Outer, OuterOrInnerPerm::Inner}) {
2113 (permType == OuterOrInnerPerm::Outer) ? getOuterPerm() : getInnerPerm();
2114 auto errorMsg = (permType == OuterOrInnerPerm::Outer)
2115 ? StringLiteral{
"invalid outer_perm"}
2116 : StringLiteral{
"invalid inner_perm"};
2117 if (!isValidPackingPermutation(packOp, perm, permType) ||
2118 !isValidPackingPermutation(unPackOp, perm, permType)) {
2120 unPackOp ? unPackOp.getOperation() : packOp.getOperation();
2121 return emitSilenceableError() << errorMsg <<
": " << *packOrUnpackOp;
2127 assert(packOp && linalgOp &&
"unexpected null op");
2131 rewriter, packOp, linalgOp, unPackOp, getOuterPerm(), getInnerPerm());
2133 assert(succeeded(res) &&
"unexpected packTranspose failure");
2136 transformResults.
set(cast<OpResult>(getPackOp()), {res->transposedPackOp});
2137 transformResults.
set(cast<OpResult>(getPackedOp()),
2138 {res->transposedLinalgOp});
2140 transformResults.
set(cast<OpResult>(getUnPackOp()),
2141 {res->transposedUnPackOp});
2143 transformResults.
set(cast<OpResult>(getUnPackOp()), {});
2158 StringRef copyBackOp,
2159 bool usePrescribedTensorShapes) {
2160 auto resultType = transform::AnyOpType::get(
b.getContext());
2166 b.getI64ArrayAttr(paddingDimensions),
2169 (padToMultipleOf.empty()
2171 :
b.getDenseI64ArrayAttr(padToMultipleOf)),
2172 b.getI64ArrayAttr(nofoldFlags),
2173 b.getArrayAttr(transposePaddings),
2174 b.getStringAttr(copyBackOp),
2176 usePrescribedTensorShapes ?
b.getUnitAttr() :
nullptr);
2184 StringRef copyBackOp,
2185 bool usePrescribedTensorShapes) {
2186 auto resultType = transform::AnyOpType::get(
b.getContext());
2190 staticPadToMultipleOf);
2196 b.getI64ArrayAttr(paddingDimensions),
2197 dynamicPadToMultipleOf,
2198 staticPadToMultipleOf,
2199 b.getI64ArrayAttr(nofoldFlags),
2200 b.getArrayAttr(transposePaddings),
2202 usePrescribedTensorShapes);
2205void PadOp::getEffects(
2213SmallVector<OpFoldResult> PadOp::getMixedPadToMultipleOf() {
2215 return getMixedValues(getStaticPadToMultipleOf(), getPadToMultipleOf(),
b);
2218DiagnosedSilenceableFailure
2219transform::PadOp::apply(transform::TransformRewriter &rewriter,
2220 transform::TransformResults &results,
2221 transform::TransformState &state) {
2222 auto transformOp = cast<TransformOpInterface>(getOperation());
2223 SmallVector<Operation *> paddedOps, padOps, copyBackOps;
2226 auto linalgTarget = dyn_cast<LinalgOp>(
target);
2227 if (!linalgTarget) {
2228 auto diag = emitSilenceableError() <<
"expected LinalgOp target";
2229 diag.attachNote(
target->getLoc()) <<
"target op";
2234 SmallVector<bool> nofoldFlags;
2235 for (int64_t packPadding :
2237 nofoldFlags.push_back(
static_cast<bool>(packPadding));
2240 SmallVector<Attribute> paddingValues;
2241 for (
auto const &[untypedAttr, elementOrTensorType] :
2242 llvm::zip(getPaddingValues(), linalgTarget->getOperandTypes())) {
2245 paddingValues.push_back(untypedAttr);
2248 auto attr = dyn_cast<TypedAttr>(untypedAttr);
2250 emitOpError(
"expects padding values to be typed attributes or poison");
2255 if (
auto stringAttr = dyn_cast<StringAttr>(attr)) {
2259 if (!parsedAttr || parsedAttr.getType() != elementType) {
2261 << elementType <<
", got " << untypedAttr;
2262 diag.attachNote(linalgTarget.getLoc()) <<
"when applied to this op";
2265 paddingValues.push_back(parsedAttr);
2269 if (attr.getType() != elementType) {
2271 << elementType <<
", got " << attr;
2272 diag.attachNote(linalgTarget.getLoc()) <<
"when applied to this op";
2275 paddingValues.push_back(attr);
2279 SmallVector<SmallVector<int64_t>> transposePaddings;
2280 for (Attribute transposeVector : cast<ArrayAttr>(getTransposePaddings()))
2282 cast<ArrayAttr>(transposeVector)));
2289 SmallVector<int64_t> padToMultipleOf;
2291 state, transformOp, getMixedPadToMultipleOf(), padToMultipleOf);
2294 if (padToMultipleOf.empty())
2296 SmallVector<int64_t>(
options.paddingDimensions.size(), 1);
2298 options.padToMultipleOf = padToMultipleOf;
2299 options.paddingValues = paddingValues;
2300 options.nofoldFlags = nofoldFlags;
2301 if (getCopyBackOp() ==
2302 bufferization::MaterializeInDestinationOp::getOperationName()) {
2303 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::
2304 BufferizationMaterializeInDestination;
2305 }
else if (getCopyBackOp() == linalg::CopyOp::getOperationName()) {
2306 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::LinalgCopy;
2307 }
else if (getCopyBackOp() == kCopyOpNone) {
2308 options.copyBackOp = LinalgPaddingOptions::CopyBackOp::None;
2310 llvm_unreachable(
"unsupported copy_back op");
2313 bool irChanged =
false;
2314 if (getUsePrescribedTensorShapes() &&
2315 linalgTarget.hasPureTensorSemantics()) {
2316 OpBuilder::InsertionGuard g(rewriter);
2318 for (OpOperand &operand : linalgTarget->getOpOperands()) {
2319 for (
auto [i, dim] : llvm::enumerate(linalgTarget.getShape(&operand))) {
2320 if (ShapedType::isStatic(dim))
2322 options.setSizeToPadTo(operand.getOperandNumber(), i,
2324 operand.get().getLoc(),
2331 SmallVector<Value> replacements;
2332 SmallVector<tensor::PadOp> newPadOps;
2334 replacements, newPadOps))) {
2340 auto diag = emitSilenceableError() <<
"failed to pad op";
2341 diag.attachNote(
target->getLoc()) <<
"target op";
2350 rewriter.
replaceOp(linalgTarget, replacements);
2351 paddedOps.push_back(paddedOp);
2352 padOps.append(newPadOps.begin(), newPadOps.end());
2353 if (
options.copyBackOp != LinalgPaddingOptions::CopyBackOp::None) {
2354 for (Value v : replacements) {
2355 Operation *copyBackOp = v.getDefiningOp();
2356 if (!llvm::is_contained(copyBackOps, copyBackOp))
2357 copyBackOps.push_back(copyBackOp);
2362 results.
set(cast<OpResult>(getPadded()), paddedOps);
2363 results.
set(cast<OpResult>(getPad()), padOps);
2364 results.
set(cast<OpResult>(getCopy()), copyBackOps);
2368LogicalResult transform::PadOp::verify() {
2369 SmallVector<int64_t> nofoldFlags =
2371 if (any_of(nofoldFlags, [](int64_t packPadding) {
2372 return packPadding != 0 && packPadding != 1;
2375 <<
"expects nofold_flags to contain booleans (0/1), found "
2376 << getNofoldFlags();
2379 SmallVector<int64_t> paddingDimensions =
2381 if (any_of(paddingDimensions,
2382 [](int64_t paddingDimension) {
return paddingDimension < 0; })) {
2383 return emitOpError() <<
"expects padding_dimensions to contain positive "
2385 << getPaddingDimensions();
2387 if (!getMixedPadToMultipleOf().empty()) {
2388 if (getMixedPadToMultipleOf().size() != paddingDimensions.size()) {
2389 return emitOpError() <<
"expects as many multiples as padding_dimensions";
2392 ArrayAttr transposes = getTransposePaddings();
2393 for (Attribute attr : transposes) {
2395 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2396 if (!std::is_permutation(sequence.begin(), sequence.end(),
2397 transpose.begin(), transpose.end())) {
2399 <<
"expects transpose_paddings to be a permutation, found "
2403 if (getCopyBackOp() !=
2404 bufferization::MaterializeInDestinationOp::getOperationName() &&
2405 getCopyBackOp() != linalg::CopyOp::getOperationName() &&
2406 getCopyBackOp() != kCopyOpNone)
2415void transform::PadTilingInterfaceOp::build(OpBuilder &
b,
2418 ArrayRef<int64_t> paddingSizes,
2419 bool padToMultipleOf) {
2420 auto resultType = transform::AnyOpType::get(
b.getContext());
2429 :
b.getDenseI64ArrayAttr(paddingSizes)),
2431 padToMultipleOf ?
b.getUnitAttr() :
nullptr);
2434void transform::PadTilingInterfaceOp::build(
2436 ArrayRef<OpFoldResult> mixedPaddingSizes,
bool padToMultipleOf) {
2437 auto resultType = transform::AnyOpType::get(
b.getContext());
2438 SmallVector<int64_t> staticPaddingSizes;
2439 SmallVector<Value> dynamicPaddingSizes;
2441 staticPaddingSizes);
2447 dynamicPaddingSizes,
2452void transform::PadTilingInterfaceOp::getEffects(
2453 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2460SmallVector<OpFoldResult>
2461transform::PadTilingInterfaceOp::getMixedPaddingSizes() {
2466DiagnosedSilenceableFailure
2467transform::PadTilingInterfaceOp::apply(transform::TransformRewriter &rewriter,
2468 transform::TransformResults &results,
2469 transform::TransformState &state) {
2470 SmallVector<Operation *> paddedOps, padOps;
2473 auto targetOp = dyn_cast<TilingInterface>(
target);
2475 auto diag = emitSilenceableError() <<
"expected TilingInterface target";
2476 diag.attachNote(
target->getLoc()) <<
"target op";
2483 if (!isa<IndexingMapOpInterface>(targetOp.getOperation())) {
2484 auto diag = emitSilenceableError() <<
"only IndexingMapOpInterface ops "
2486 diag.attachNote(
target->getLoc()) <<
"target op";
2491 SmallVector<Attribute> paddingValues;
2492 for (
auto const &[untypedAttr, elementOrTensorType] :
2493 llvm::zip(getPaddingValues(), targetOp->getOperandTypes())) {
2494 auto attr = dyn_cast<TypedAttr>(untypedAttr);
2498 paddingValues.push_back(untypedAttr);
2502 emitOpError(
"expects padding values to be typed attributes or poison");
2506 if (
auto stringAttr = dyn_cast<StringAttr>(attr)) {
2510 if (!parsedAttr || parsedAttr.getType() != elementType) {
2512 << elementType <<
", got " << attr;
2513 diag.attachNote(targetOp.getLoc()) <<
"when applied to this op";
2516 paddingValues.push_back(parsedAttr);
2520 if (attr.getType() != elementType) {
2522 << elementType <<
", got " << attr;
2523 diag.attachNote(targetOp.getLoc()) <<
"when applied to this op";
2526 paddingValues.push_back(attr);
2530 PadTilingInterfaceOptions
options;
2531 options.setPaddingValues(paddingValues)
2532 .setPaddingSizes(getMixedPaddingSizes())
2533 .setPadToMultipleOf(getPadToMultipleOf());
2535 OpBuilder::InsertionGuard g(rewriter);
2538 rewriter, cast<TilingInterface>(targetOp.getOperation()),
options);
2539 if (
failed(maybePadOps)) {
2540 auto diag = emitSilenceableError() <<
"failed to pad op";
2541 diag.attachNote(
target->getLoc()) <<
"target op";
2544 const auto &[paddedOperands, paddedOp, slicedResults] = maybePadOps.value();
2547 paddedOps.push_back(paddedOp);
2548 padOps.append(paddedOperands.begin(), paddedOperands.end());
2549 rewriter.
replaceOp(targetOp.getOperation(), slicedResults);
2552 results.
set(cast<OpResult>(getPadded()), paddedOps);
2553 results.
set(cast<OpResult>(getPad()), padOps);
2557LogicalResult transform::PadTilingInterfaceOp::verify() {
return success(); }
2563DiagnosedSilenceableFailure transform::HoistPadBuildPackingLoopNestOp::apply(
2564 transform::TransformRewriter &rewriter,
2565 transform::TransformResults &transformResults,
2566 transform::TransformState &state) {
2569 if (!llvm::hasSingleElement(targetOps) || !llvm::hasSingleElement(loopOps)) {
2571 <<
"requires exactly one target and one loop handle (got "
2572 << llvm::range_size(targetOps) <<
" and "
2573 << llvm::range_size(loopOps) <<
")";
2576 auto padOp = dyn_cast_or_null<tensor::PadOp>(*targetOps.begin());
2577 auto loopOp = dyn_cast_or_null<scf::ForOp>(*loopOps.begin());
2578 if (!padOp || !loopOp)
2581 FailureOr<linalg::detail::PackingResult>
result =
2587 if (
result->clonedLoopIvs.empty()) {
2588 transformResults.
set(cast<OpResult>(getPackingLoop()),
2589 {
result->hoistedPadOp.getOperation()});
2592 auto outerPackedLoop =
2594 transformResults.
set(cast<OpResult>(getPackingLoop()),
2595 {outerPackedLoop.getOperation()});
2599LogicalResult transform::HoistPadBuildPackingLoopNestOp::verify() {
2600 ArrayRef<int64_t> transpose = getTranspose();
2601 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2602 if (!std::is_permutation(sequence.begin(), sequence.end(), transpose.begin(),
2604 return emitOpError() <<
"expects transpose to be a permutation, found "
2610void transform::HoistPadBuildPackingLoopNestOp::getEffects(
2611 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2618DiagnosedSilenceableFailure
2619transform::HoistPadOp::applyToOne(transform::TransformRewriter &rewriter,
2621 transform::ApplyToEachResultList &results,
2622 transform::TransformState &state) {
2623 tensor::PadOp hoistedPadOp;
2624 SmallVector<TransposeOp> transposeOps;
2625 FailureOr<Value>
result =
2627 hoistedPadOp, transposeOps);
2638 return emitDefaultSilenceableFailure(
target);
2641LogicalResult transform::HoistPadOp::verify() {
2642 ArrayRef<int64_t> transpose = getTranspose();
2643 auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size()));
2644 if (!std::is_permutation(sequence.begin(), sequence.end(), transpose.begin(),
2646 return emitOpError() <<
"expects transpose to be a permutation, found "
2656DiagnosedSilenceableFailure
2657transform::PromoteOp::applyToOne(transform::TransformRewriter &rewriter,
2659 transform::ApplyToEachResultList &results,
2660 transform::TransformState &state) {
2661 LinalgPromotionOptions promotionOptions;
2662 if (!getOperandsToPromote().empty())
2665 if (getUseFullTilesByDefault())
2667 getUseFullTilesByDefault());
2668 if (getUseOriginalSubviewSize())
2672 promotionOptions = promotionOptions.
setUseAlloca(getUseAlloca());
2673 if (!getUseFullTileBuffers().empty())
2675 llvm::to_vector(getUseFullTileBuffers().getAsValueRange<BoolAttr>()));
2676 if (getAlignment().has_value())
2677 promotionOptions = promotionOptions.
setAlignment(*getAlignment());
2678 if (getMemorySpace().has_value())
2679 promotionOptions = promotionOptions.
setMemorySpace(*getMemorySpace());
2681 if (getMapping().has_value()) {
2683 auto mapping = *getMapping();
2684 if (mapping.size() > 1)
2685 return emitDefaultDefiniteFailure(
target);
2687 auto addressSpace = cast<mlir::gpu::GPUMemorySpaceMappingAttr>(mapping[0]);
2689 if (addressSpace.getAddressSpace() ==
2690 mlir::gpu::GPUDialect::getWorkgroupAddressSpace()) {
2697 }
else if (addressSpace.getAddressSpace() ==
2698 mlir::gpu::GPUDialect::getPrivateAddressSpace()) {
2706 return emitDefaultDefiniteFailure(
target);
2711 return emitDefaultDefiniteFailure(
target);
2716 return emitDefaultDefiniteFailure(
target);
2725DiagnosedSilenceableFailure
2726transform::ReplaceOp::apply(transform::TransformRewriter &rewriter,
2727 TransformResults &transformResults,
2728 TransformState &state) {
2732 for (Operation *
target : payload) {
2733 if (
target->getNumOperands() > 0)
2735 if (!
target->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
2736 target->getNumRegions() > 0)
2738 <<
"expected target that is isolated from above";
2742 Operation *pattern = &getBodyRegion().front().front();
2743 SmallVector<Operation *> replacements;
2744 for (Operation *
target : payload) {
2745 if (getOperation()->isAncestor(
target))
2752 transformResults.
set(cast<OpResult>(getReplacement()), replacements);
2756void transform::ReplaceOp::getEffects(
2757 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
2763LogicalResult transform::ReplaceOp::verify() {
2764 if (!getBodyRegion().hasOneBlock())
2766 if (std::distance(getBodyRegion().front().begin(),
2767 getBodyRegion().front().end()) != 1)
2768 return emitOpError() <<
"expected one operation in block";
2769 Operation *
replacement = &getBodyRegion().front().front();
2772 <<
"expected replacement without operands";
2773 if (!
replacement->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
2776 <<
"expect op that is isolated from above";
2784DiagnosedSilenceableFailure
2785transform::ScalarizeOp::applyToOne(transform::TransformRewriter &rewriter,
2787 transform::ApplyToEachResultList &results,
2788 transform::TransformState &state) {
2789 scf::SCFTilingOptions tilingOptions;
2790 tilingOptions.setTileSizeComputationFunction([&](OpBuilder &
b, Operation *) {
2791 SmallVector<OpFoldResult> tileSizes;
2792 Location loc =
target.getLoc();
2793 SmallVector<OpFoldResult> allShapeSizes =
2794 target.createFlatListOfOperandDims(
b, loc);
2795 AffineMap map =
target.getShapesToLoopsMap();
2798 SmallVector<OpFoldResult> shapeSizes =
2803 for (OpFoldResult shapeSize : shapeSizes) {
2805 :
b.getIndexAttr(1));
2810 FailureOr<scf::SCFTilingResult> maybeTilingResult =
tileUsingSCF(
2811 rewriter, cast<TilingInterface>(
target.getOperation()), tilingOptions);
2812 if (
failed(maybeTilingResult))
2813 return emitDefaultDefiniteFailure(
target);
2815 if (
target->getNumResults())
2820 results.
reserve(maybeTilingResult->tiledOps.size());
2821 for (Operation *tiled : maybeTilingResult->tiledOps)
2830DiagnosedSilenceableFailure
2831transform::ConvertToLoopsOp::apply(transform::TransformRewriter &rewriter,
2832 transform::TransformResults &results,
2833 transform::TransformState &state) {
2834 SmallVector<Operation *> loops;
2836 auto tilingOp = dyn_cast<TilingInterface>(*
target);
2838 DiagnosedSilenceableFailure
diag =
2839 emitSilenceableError()
2840 <<
"expected the payload to implement TilingInterface";
2841 diag.attachNote(
target->getLoc()) <<
"payload op";
2845 FailureOr<SmallVector<scf::ForOp>> generatedLoops =
2846 scf::lowerToLoopsUsingSCFForOp(rewriter, tilingOp);
2847 if (
failed(generatedLoops))
2848 return emitDefaultDefiniteFailure(
target);
2849 for (scf::ForOp &loop : *generatedLoops) {
2850 loops.push_back(loop.getOperation());
2854 results.
set(cast<OpResult>(getResult()), loops);
2862DiagnosedSilenceableFailure
2863transform::RewriteInDestinationPassingStyleOp::applyToOne(
2864 transform::TransformRewriter &rewriter, Operation *
target,
2865 transform::ApplyToEachResultList &results,
2866 transform::TransformState &state) {
2868 FailureOr<Operation *> maybeResult =
2870 .Case<DestinationStyleOpInterface>([](
auto op) {
return op; })
2871 .Case<tensor::FromElementsOp, tensor::GenerateOp, tensor::PadOp>(
2872 [&rewriter](
auto op) {
2876 return emitDefaultSilenceableFailure(
target);
2885DiagnosedSilenceableFailure
2886SplitOp::apply(transform::TransformRewriter &rewriter,
2887 TransformResults &results, TransformState &state) {
2889 SmallVector<Operation *> payload =
2892 bool isMultiwaySplit = getMultiway();
2894 if (isMultiwaySplit && !llvm::hasSingleElement(payload)) {
2896 <<
"requires exactly one target when "
2897 "multiway split is enabled (got "
2898 << llvm::range_size(payload) <<
")";
2901 SmallVector<OpFoldResult> chunkSizes;
2903 if (!isMultiwaySplit)
2904 chunkSizes.reserve(payload.size());
2906 if (getDynamicChunkSizes()) {
2908 if (isa<TransformHandleTypeInterface>(getDynamicChunkSizes().
getType())) {
2909 chunkSizes = llvm::map_to_vector(
2910 state.
getPayloadOps(getDynamicChunkSizes()), [&](Operation *op) {
2913 diag = emitSilenceableError()
2914 <<
"expected dynamic split point handle to point to a "
2915 "single-result index-typed op";
2916 diag.attachNote(op->
getLoc()) <<
"dynamic split point";
2921 chunkSizes = llvm::map_to_vector(
2922 state.
getParams(getDynamicChunkSizes()),
2923 [](Attribute attr) {
return OpFoldResult(attr); });
2925 if (
diag.isSilenceableFailure())
2930 if (!isMultiwaySplit && chunkSizes.size() != payload.size()) {
2932 <<
"expected the dynamic split point handle to point to as "
2934 << chunkSizes.size() <<
") as the target handle ("
2935 << payload.size() <<
")";
2938 chunkSizes.resize(payload.size(),
2942 auto checkStructuredOpAndDimensions =
2943 [&](LinalgOp linalgOp, Location loc) -> DiagnosedSilenceableFailure {
2945 auto diag = emitSilenceableError() <<
"only applies to structured ops";
2946 diag.attachNote(loc) <<
"target op";
2950 if (getDimension() >= linalgOp.getNumLoops()) {
2951 auto diag = emitSilenceableError() <<
"dimension " << getDimension()
2952 <<
" does not exist in target op";
2953 diag.attachNote(loc) <<
"target op";
2959 auto checkFailureInSplitting =
2960 [&](
bool hasFailed, Location loc) -> DiagnosedSilenceableFailure {
2969 SmallVector<Operation *> opList;
2970 if (isMultiwaySplit) {
2973 TilingInterface head, tail;
2974 Operation *
target = payload.front();
2976 LinalgOp linalgOp = dyn_cast<LinalgOp>(
target);
2979 DiagnosedSilenceableFailure
diag =
2980 checkStructuredOpAndDimensions(linalgOp,
target->getLoc());
2981 if (
diag.isSilenceableFailure())
2984 for (
auto &&[idx, chunkSize] : llvm::enumerate(chunkSizes)) {
2987 target = tail.getOperation();
2992 linalgOp = cast<LinalgOp>(
target);
2993 Location loc =
target->getLoc();
2997 rewriter, cast<TilingInterface>(linalgOp.getOperation()),
2998 getDimension(), chunkSize);
3001 DiagnosedSilenceableFailure
diag =
3002 checkFailureInSplitting(!head && !tail, loc);
3003 if (
diag.isDefiniteFailure())
3006 opList.push_back(head.getOperation());
3011 opList.push_back(tail.getOperation());
3015 SmallVector<Operation *> first, second;
3016 Operation *noSecondPart =
nullptr;
3017 for (
const auto &pair : llvm::zip(payload, chunkSizes)) {
3018 Operation *
target = std::get<0>(pair);
3019 Location loc =
target->getLoc();
3020 LinalgOp linalgOp = dyn_cast<LinalgOp>(
target);
3021 DiagnosedSilenceableFailure
diag =
3022 checkStructuredOpAndDimensions(linalgOp,
target->getLoc());
3024 if (
diag.isSilenceableFailure())
3028 std::tie(first.emplace_back(), second.emplace_back()) =
linalg::splitOp(
3029 rewriter, cast<TilingInterface>(linalgOp.getOperation()),
3030 getDimension(), std::get<1>(pair));
3033 DiagnosedSilenceableFailure diagSplit =
3034 checkFailureInSplitting(!first.back() && !second.back(), loc);
3039 if (!second.back()) {
3045 if (second.size() != first.size() && !second.empty()) {
3046 auto diag = emitSilenceableError()
3047 <<
"splitting does not produce the second part for a subset "
3050 <<
"expected splitting to produce the second part of all "
3051 "or none of the targets";
3053 <<
"first target with no second part";
3057 opList.append(first);
3058 if (!second.empty())
3059 opList.append(second);
3061 results.
set(cast<OpResult>(getSplitList()), opList);
3065void SplitOp::getEffects(
3066 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3068 if (getDynamicChunkSizes())
3074ParseResult SplitOp::parse(OpAsmParser &parser, OperationState &
result) {
3075 OpAsmParser::UnresolvedOperand
target, dynamicChunkSizes;
3076 IntegerAttr staticChunkSizes;
3080 OptionalParseResult dynamicPointParseResult =
3082 if (!dynamicPointParseResult.
has_value()) {
3083 int64_t staticChunkSizesValue;
3097 if (dynamicPointParseResult.
has_value()) {
3098 Type chunkSizesType;
3111 SplitOp::getStaticChunkSizesAttrName(
result.name).getValue(),
3113 result.addTypes(targetType);
3117void SplitOp::print(OpAsmPrinter &printer) {
3118 printer <<
" " << getTarget() <<
" after ";
3119 int64_t staticChunkSize =
static_cast<int64_t
>(getStaticChunkSizes());
3120 if (staticChunkSize != ShapedType::kDynamic)
3121 printer << staticChunkSize;
3123 printer << getDynamicChunkSizes();
3125 NamedAttrList attrs(getOperation()->getDiscardableAttrDictionary());
3126 attrs.append(getDimensionAttrName(), getDimensionAttr());
3127 if (UnitAttr multiway = getMultiwayAttr())
3128 attrs.append(getMultiwayAttrName(), multiway);
3130 printer <<
" : " << getTarget().getType();
3131 if (staticChunkSize == ShapedType::kDynamic)
3132 printer <<
", " << getDynamicChunkSizes().getType();
3135LogicalResult SplitOp::verify() {
3136 if ((
static_cast<int64_t
>(getStaticChunkSizes()) != ShapedType::kDynamic) ^
3137 (getDynamicChunkSizes() ==
nullptr)) {
3138 return emitOpError() <<
"expects either a dynamic or a static split "
3139 "point to be provided";
3148void transform::SplitReductionOp::build(
3149 OpBuilder &builder, OperationState &
result, Value
target,
3150 int64_t splitFactor, int64_t insertSplitDimension,
bool innerParallel,
3151 bool useScalingAlgorithm,
bool useAlloc) {
3154 result.addAttribute(SplitReductionOp::getSplitFactorAttrName(
result.name),
3157 SplitReductionOp::getInsertSplitDimensionAttrName(
result.name),
3159 if (innerParallel) {
3160 result.addAttribute(SplitReductionOp::getInnerParallelAttrName(
result.name),
3163 if (useScalingAlgorithm) {
3165 SplitReductionOp::getUseScalingAlgorithmAttrName(
result.name),
3169 result.addAttribute(SplitReductionOp::getUseAllocAttrName(
result.name),
3172 auto resultType = transform::AnyOpType::get(ctx);
3173 result.addTypes({resultType, resultType, resultType, resultType});
3176DiagnosedSilenceableFailure transform::SplitReductionOp::applyToOne(
3177 transform::TransformRewriter &rewriter, LinalgOp
target,
3178 transform::ApplyToEachResultList &results,
3179 transform::TransformState &state) {
3181 return linalg::SplitReductionOptions{int64_t(getSplitFactor()),
3182 unsigned(getInsertSplitDimension()),
3183 bool(getInnerParallel())};
3186 FailureOr<SplitReductionResult> splitResult =
3187 (getUseScalingAlgorithm())
3191 return emitDefaultDefiniteFailure(
target);
3193 results.
push_back(splitResult->initOrAlloc);
3195 results.
push_back(splitResult->splitLinalgOp);
3196 results.
push_back(splitResult->resultCombiningLinalgOp);
3204void transform::TileReductionUsingForOp::build(
3205 OpBuilder &builder, OperationState &
result, Value
target,
3206 ArrayRef<int64_t> staticTileSizes) {
3213 auto opTy = transform::AnyOpType::get(ctx);
3219 staticTileSizesAttr);
3222DiagnosedSilenceableFailure transform::TileReductionUsingForOp::applyToOne(
3223 transform::TransformRewriter &rewriter, Operation *
target,
3224 transform::ApplyToEachResultList &results,
3225 transform::TransformState &state) {
3228 auto partialReductionOp = dyn_cast<PartialReductionOpInterface>(
target);
3229 if (!partialReductionOp) {
3232 "Operation should implement PartialReductionOpInterface");
3235 SmallVector<unsigned> reductionDims =
3237 if (reductionDims.empty()) {
3238 for (
auto [idx, iteratorType] :
3239 llvm::enumerate(partialReductionOp.getLoopIteratorTypes())) {
3240 if (iteratorType == utils::IteratorType::reduction)
3241 reductionDims.push_back(idx);
3245 scf::SCFTilingOptions
options;
3246 options.setLoopType(scf::SCFTilingOptions::LoopType::ForOp);
3247 options.setReductionTilingStrategy(
3250 options.setReductionDims(reductionDims);
3251 FailureOr<scf::SCFTilingResult>
result =
3252 scf::tileUsingSCF(rewriter, partialReductionOp,
options);
3256 "failed to tile using partial reduction");
3259 for (Value initValue :
result->initialValues)
3261 for (
auto *parallelTiledOp :
result->tiledOps)
3263 for (
auto *mergeOp :
result->mergeOps)
3273void transform::TileReductionUsingForallOp::build(
3274 OpBuilder &builder, OperationState &
result, Value
target,
3275 ArrayRef<int64_t> staticNumThreads, ArrayRef<int64_t> staticTileSizes,
3283 auto opTy = transform::AnyOpType::get(ctx);
3290 staticNumThreadsAttr,
3291 staticTileSizesAttr,
3295DiagnosedSilenceableFailure transform::TileReductionUsingForallOp::applyToOne(
3296 transform::TransformRewriter &rewriter, Operation *
target,
3297 transform::ApplyToEachResultList &results,
3298 transform::TransformState &state) {
3301 auto partialReductionOp = dyn_cast<PartialReductionOpInterface>(
target);
3302 if (!partialReductionOp) {
3305 "Operation should implement PartialReductionOpInterface");
3307 SmallVector<OpFoldResult> numThreads =
3309 SmallVector<OpFoldResult> tileSizes =
3312 scf::SCFTilingOptions
options;
3313 options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
3314 options.setReductionTilingStrategy(
3316 if (!getNumThreads().empty()) {
3317 options.setNumThreads(numThreads);
3319 options.setTileSizes(tileSizes);
3321 if (
auto mapping = getMapping()) {
3322 options.setMapping(mapping.value().getValue());
3324 SmallVector<unsigned> reductionDims =
3326 if (reductionDims.empty()) {
3327 for (
auto [idx, iteratorType] :
3328 llvm::enumerate(partialReductionOp.getLoopIteratorTypes())) {
3329 if (iteratorType == utils::IteratorType::reduction)
3330 reductionDims.push_back(idx);
3333 options.setReductionDims(reductionDims);
3334 FailureOr<scf::SCFTilingResult>
result =
3335 scf::tileUsingSCF(rewriter, partialReductionOp,
options);
3338 auto diag = emitSilenceableError() <<
"could not tile reduction";
3343 for (Value initValue :
result->initialValues)
3345 for (
auto *parallelTiledOp :
result->tiledOps)
3347 for (
auto *mergeOp :
result->mergeOps)
3357DiagnosedSilenceableFailure
3358transform::ContinuousTileSizesOp::apply(transform::TransformRewriter &rewriter,
3359 TransformResults &transformResults,
3360 TransformState &state) {
3362 SmallVector<Operation *> targetOps =
3365 if (!llvm::hasSingleElement(targetOps)) {
3367 <<
"requires exactly one target (got " << llvm::range_size(targetOps)
3371 Operation *
target = *targetOps.begin();
3372 auto linalgOp = dyn_cast<LinalgOp>(
target);
3373 auto tileableOp = dyn_cast<TilingInterface>(
target);
3378 OpBuilder builder(linalgOp.getContext());
3380 if (isa<TransformParamTypeInterface>(getChunkSizes().
getType())) {
3381 if (linalgOp.hasDynamicShape()) {
3382 auto diag = emitSilenceableError()
3383 <<
"cannot compute parametric tile sizes for dynamically "
3384 "shaped payload op";
3385 diag.attachNote(linalgOp->getLoc()) <<
"payload op";
3389 FailureOr<StaticContinuousTileSizeSpecification> spec =
3393 return emitSilenceableError()
3394 <<
"failed to compute multi-size tiling sizes";
3397 SmallVector<int64_t> chunkSizes;
3399 for (
auto &&[tileSize, tripCount] :
3400 llvm::zip_equal(spec->tileSizes, spec->tripCounts))
3401 chunkSizes.push_back(tileSize * tripCount);
3403 auto getI64AttrsFromI64 = [&](ArrayRef<int64_t> values) {
3404 return llvm::map_to_vector(values, [&](int64_t value) -> Attribute {
3409 getI64AttrsFromI64(spec->tileSizes));
3410 transformResults.
setParams(cast<OpResult>(getChunkSizes()),
3411 getI64AttrsFromI64(chunkSizes));
3418 OpFoldResult targetSize = builder.
getIndexAttr(getTargetSize());
3419 unsigned dimension = getDimension();
3422 builder, tileableOp, dimension, targetSize,
true);
3424 return emitSilenceableError() <<
"could not generate tile size computation";
3429 auto apply = [&](AffineExpr expr, ArrayRef<OpFoldResult> ofrs) -> Value {
3434 SmallVector<Value> chunkSizes;
3436 for (
auto &&[tileSize, tripCount] :
3437 llvm::zip_equal(spec->tileSizes, spec->tripCounts)) {
3438 splitPoint = apply(s0 * s1, {tileSize, tripCount});
3439 chunkSizes.push_back(splitPoint);
3442 auto getDefiningOps = [&](ArrayRef<Value> values) {
3443 return llvm::map_to_vector(values, [&](Value value) -> Operation * {
3449 getDefiningOps(spec->tileSizes));
3450 transformResults.
set(cast<OpResult>(getChunkSizes()),
3451 getDefiningOps(chunkSizes));
3456LogicalResult transform::ContinuousTileSizesOp::verify() {
3459 return emitOpError() <<
"expects all results type to be the same";
3465void transform::ContinuousTileSizesOp::getEffects(
3466 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3483 Type &tileSizesType,
3484 Type &chunkSizesType) {
3485 FunctionType funcType;
3487 if (failed(parser.
parseType<FunctionType>(funcType)))
3490 if (funcType.getNumInputs() != 1 || funcType.getNumResults() != 1) {
3491 parser.
emitError(typeLoc) <<
"expects a trailing functional type with one "
3492 "argument and one result";
3494 targetType = funcType.getInput(0);
3495 tileSizesType = chunkSizesType = funcType.getResult(0);
3504void transform::TileUsingForOp::build(
3506 Value
target, ArrayRef<int64_t> staticTileSizes,
3507 ArrayRef<int64_t> interchange,
3508 std::optional<ArrayRef<bool>> scalableSizes) {
3509 return build(builder,
result, loopTypes,
3513 interchange, scalableSizes);
3516void transform::TileUsingForOp::build(
3517 OpBuilder &builder, OperationState &
result, Value
target,
3518 ArrayRef<int64_t> staticTileSizes, ArrayRef<int64_t> interchange,
3519 std::optional<ArrayRef<bool>> scalableSizes) {
3522 interchange, scalableSizes);
3525void transform::TileUsingForOp::build(
3526 OpBuilder &builder, OperationState &
result, Value
target,
3527 ArrayRef<OpFoldResult> mixedTileSizes, ArrayRef<int64_t> interchange,
3528 std::optional<ArrayRef<bool>> scalableSizes) {
3531 SmallVector<Type> loopTypes(1, builder.
getType<transform::AnyOpType>());
3532 build(builder,
result, loopTypes,
target, mixedTileSizes, interchange,
3536void transform::TileUsingForOp::build(
3537 OpBuilder &builder, OperationState &
result, Value
target,
3538 ArrayRef<OpFoldResult> mixedTileSizes,
3539 ArrayRef<OpFoldResult> mixedInterchange,
3540 std::optional<ArrayRef<bool>> scalableSizes) {
3543 SmallVector<Type> loopTypes(1, builder.
getType<transform::AnyOpType>());
3544 build(builder,
result, loopTypes,
target, mixedTileSizes, mixedInterchange,
3548void transform::TileUsingForOp::build(
3550 Value
target, ArrayRef<OpFoldResult> mixedTileSizes,
3551 ArrayRef<int64_t> interchange,
3552 std::optional<ArrayRef<bool>> scalableSizes) {
3553 SmallVector<OpFoldResult> mixedInterchange =
3555 build(builder,
result, loopTypes,
target, mixedTileSizes, mixedInterchange,
3559void transform::TileUsingForOp::build(
3561 Value
target, ArrayRef<OpFoldResult> mixedTileSizes,
3562 ArrayRef<OpFoldResult> mixedInterchange,
3563 std::optional<ArrayRef<bool>> scalableSizes) {
3564 SmallVector<int64_t> staticTileSizes;
3565 SmallVector<Value> dynamicTileSizes;
3566 SmallVector<int64_t> staticInterchange;
3567 SmallVector<Value> dynamicInterchange;
3576 unsigned numExpectedLoops =
3577 staticTileSizes.size() - llvm::count(staticTileSizes, 0);
3578 SmallVector<Type> resultTypes;
3579 resultTypes.reserve(numExpectedLoops);
3580 assert((loopTypes.size() == 1 || loopTypes.size() == numExpectedLoops) &&
3581 "expected one loop type or as many as loops");
3582 if (loopTypes.size() == 1)
3583 resultTypes.append(numExpectedLoops, loopTypes[0]);
3585 llvm::append_range(resultTypes, loopTypes);
3586 SmallVector<bool> expandedScalableSizes(mixedTileSizes.size(),
false);
3587 if (scalableSizes.has_value())
3588 expandedScalableSizes.assign(scalableSizes->begin(), scalableSizes->end());
3589 Value packedTileSizes;
3597 staticTileSizesAttr,
3598 staticInterchangeAttr,
3599 expandedScalableSizes);
3602LogicalResult transform::TileUsingForOp::verify() {
3603 bool hasPackedTiles = getPackedTileSizes() != Value();
3604 bool hasPackedInterchange = getPackedInterchange() != Value();
3607 "tile_sizes and packed_tile_sizes are mutually exclusive");
3608 if (!getMixedInterchange().empty() && hasPackedInterchange)
3610 "interchange and packed_interchange are mutually exclusive");
3611 if (hasPackedTiles && !getScalableSizes().empty())
3613 "scalable tile_sizes are not supported with packed_tile_sizes");
3616 return emitOpError(
"expected same number of sizes (")
3618 << getScalableSizes().size() <<
")";
3620 auto iterspaceRank = getStaticSizes().size();
3621 ArrayRef<int64_t> permutation = getStaticInterchange();
3622 if (permutation.size() > iterspaceRank)
3624 <<
"interchange length exceeds iteration space dimensions ("
3625 << iterspaceRank <<
"), found " << getInterchange();
3626 SmallVector<bool> seen(iterspaceRank,
false);
3627 for (int64_t v : permutation) {
3628 if (!ShapedType::isDynamic(v)) {
3629 if (v < 0 || v >=
static_cast<int64_t
>(iterspaceRank))
3630 return emitOpError() <<
"expects interchange values to be in range [0, "
3631 << iterspaceRank <<
"), found: " << v;
3633 return emitOpError() <<
"found duplicate interchange value: " << v;
3638 ArrayRef<int64_t> staticSizes = getStaticSizes();
3639 unsigned numExpectedLoops =
3640 hasPackedTiles ? 1 : staticSizes.size() - llvm::count(staticSizes, 0);
3641 if (getLoops().size() != numExpectedLoops)
3642 return emitOpError(
"expected number of loops to tile (")
3643 << numExpectedLoops <<
") to match number of `loops` results ("
3644 << getLoops().size() <<
")";
3648DiagnosedSilenceableFailure
3649transform::TileUsingForOp::apply(transform::TransformRewriter &rewriter,
3650 TransformResults &transformResults,
3651 TransformState &state) {
3652 ArrayRef<int64_t> tileSizes = getStaticSizes();
3653 bool hasPackedTiles = getPackedTileSizes() != Value();
3654 bool hasPackedInterchange = getPackedInterchange() != Value();
3655 auto transformOp = cast<TransformOpInterface>(getOperation());
3657 SmallVector<OpFoldResult> mixedInterchange;
3658 if (hasPackedInterchange) {
3659 DiagnosedSilenceableFailure status =
3661 state, transformOp, mixedInterchange, getPackedInterchange());
3665 mixedInterchange = getMixedInterchange();
3667 SmallVector<int64_t> tileInterchange;
3669 state, transformOp, mixedInterchange, tileInterchange);
3673 SmallVector<Operation *> targets =
3675 SmallVector<SmallVector<Operation *>> dynamicSizeProducers;
3676 SmallVector<SmallVector<int64_t>> paramSizes;
3677 SmallVector<OpFoldResult> mixedTileSizes;
3678 if (hasPackedTiles) {
3680 state, transformOp, mixedTileSizes, getPackedTileSizes());
3688 if (isa<TransformParamTypeInterface>(transformValue.getType())) {
3689 dynamicSizeProducers.push_back({});
3690 ArrayRef<Attribute> params = state.
getParams(transformValue);
3691 paramSizes.push_back(llvm::map_to_vector(params, [](Attribute attr) {
3692 return cast<IntegerAttr>(attr).getValue().getSExtValue();
3695 if (paramSizes.back().size() != targets.size()) {
3696 DiagnosedSilenceableFailure
diag =
3697 emitSilenceableError()
3698 <<
"expected as many parameter values ("
3699 << dynamicSizeProducers.back().size() <<
") as target ops ("
3700 << targets.size() <<
")";
3701 diag.attachNote(transformValue.getLoc()) <<
"for this parameter";
3707 paramSizes.push_back({});
3708 dynamicSizeProducers.push_back(
3711 if (dynamicSizeProducers.back().size() != targets.size()) {
3712 DiagnosedSilenceableFailure
diag =
3713 emitSilenceableError()
3714 <<
"expected as many dynamic size-producing operations ("
3715 << dynamicSizeProducers.back().size() <<
") as target ops ("
3716 << targets.size() <<
")";
3717 diag.attachNote(transformValue.getLoc()) <<
"for this handle";
3721 for (Operation *op : dynamicSizeProducers.back()) {
3727 DiagnosedSilenceableFailure
diag =
3728 emitSilenceableError() <<
"expected sizes to be produced by ops "
3729 "with a single index-type result";
3730 diag.attachNote(op->
getLoc()) <<
"size producer op";
3731 diag.attachNote(transformValue.getLoc()) <<
"for this handle";
3737 SmallVector<Operation *> tiled;
3738 SmallVector<SmallVector<Operation *, 4>, 4> loops;
3741 ? llvm::count_if(mixedTileSizes,
3742 [](OpFoldResult ofr) {
3743 if (
auto attr = dyn_cast<Attribute>(ofr))
3744 return cast<IntegerAttr>(attr).getInt() != 0;
3747 : getLoops().size();
3748 loops.resize(numLoops);
3749 auto scalableSizes = getScalableSizes();
3750 for (
auto [i, op] : llvm::enumerate(targets)) {
3751 auto tilingInterface = dyn_cast<TilingInterface>(op);
3752 if (!tilingInterface) {
3753 DiagnosedSilenceableFailure
diag =
3754 emitSilenceableError()
3755 <<
"only ops implementing TilingInterface are supported";
3756 diag.attachNote(op->
getLoc()) <<
"target op";
3760 int64_t iterspaceRank = tilingInterface.getLoopIteratorTypes().size();
3761 if (tileInterchange.size() >
static_cast<size_t>(iterspaceRank)) {
3762 return emitSilenceableError()
3763 <<
"interchange length exceeds iteration space dimensions ("
3764 << iterspaceRank <<
")";
3766 SmallVector<bool> seen(iterspaceRank,
false);
3767 for (int64_t v : tileInterchange) {
3768 if (v < 0 || v >= iterspaceRank) {
3769 return emitSilenceableError()
3770 <<
"expects interchange values to be in range [0, "
3771 << iterspaceRank <<
"), found: " << v;
3774 return emitSilenceableError()
3775 <<
"found duplicate interchange value: " << v;
3780 if (tileSizes.size() > tilingInterface.getLoopIteratorTypes().size()) {
3781 DiagnosedSilenceableFailure
diag =
3782 emitSilenceableError()
3783 <<
"too many tiles provided, expected at most "
3784 << tilingInterface.getLoopIteratorTypes().size() <<
" found "
3785 << tileSizes.size();
3786 diag.attachNote(op->
getLoc()) <<
"target op";
3790 scf::SCFTilingOptions tilingOptions;
3791 if (!hasPackedTiles && tileSizes.empty()) {
3792 tilingOptions.setTileSizeComputationFunction(
3793 [](OpBuilder &, Operation *) -> SmallVector<OpFoldResult> {
3796 }
else if (hasPackedTiles) {
3797 tilingOptions.setTileSizes(mixedTileSizes);
3799 tilingOptions.setTileSizeComputationFunction([&, index = i](OpBuilder &
b,
3801 SmallVector<OpFoldResult> sizes;
3802 sizes.reserve(tileSizes.size());
3803 unsigned dynamicIdx = 0;
3805 for (
auto [ofrIdx, ofr] : llvm::enumerate(
getMixedSizes())) {
3806 if (
auto attr = llvm::dyn_cast_if_present<Attribute>(ofr)) {
3807 if (scalableSizes[ofrIdx]) {
3809 b, getLoc(), cast<IntegerAttr>(attr).getInt());
3811 vector::VectorScaleOp::create(
b, getLoc(),
b.getIndexType());
3813 arith::MulIOp::create(
b, getLoc(), val, vscale).getResult());
3815 sizes.push_back(attr);
3819 ArrayRef<Operation *> dynamicSizes = dynamicSizeProducers[dynamicIdx];
3820 ArrayRef<int64_t> params = paramSizes[dynamicIdx];
3822 assert((dynamicSizes.empty() ^ params.empty()) &&
3823 "expected either dynamic sizes or parameters");
3824 if (!params.empty()) {
3825 sizes.push_back(
b.getIndexAttr(params[index]));
3827 sizes.push_back(dynamicSizes[index]->getResult(0));
3834 tilingOptions.setInterchange(tileInterchange);
3835 tilingOptions.setInnerTileAlignments(
3837 FailureOr<scf::SCFTilingResult> maybeTilingResult =
3838 tileUsingSCF(rewriter, tilingInterface, tilingOptions);
3839 if (
failed(maybeTilingResult))
3842 rewriter.
replaceOp(op, maybeTilingResult->replacements);
3844 tiled.append(maybeTilingResult->tiledOps);
3845 for (
const auto &en2 : llvm::enumerate(maybeTilingResult->loops))
3846 loops[en2.index()].push_back(en2.value());
3849 transformResults.
set(cast<OpResult>(getTiledLinalgOp()), tiled);
3850 if (hasPackedTiles) {
3852 SmallVector<Operation *> flattenedLoops;
3853 for (
auto [targetIdx, _] : llvm::enumerate(targets))
3854 for (
auto [loopIdx, __] : llvm::enumerate(loops))
3855 flattenedLoops.push_back(loops[loopIdx][targetIdx]);
3856 transformResults.
set(cast<OpResult>(getLoops().front()), flattenedLoops);
3858 for (
const auto &en : llvm::enumerate(loops))
3859 transformResults.
set(cast<OpResult>(getLoops()[en.index()]), en.value());
3865SmallVector<OpFoldResult> transform::TileUsingForOp::getMixedSizes() {
3867 ArrayRef<int64_t> tileSizes = getStaticSizes();
3868 SmallVector<OpFoldResult> results;
3869 results.reserve(tileSizes.size());
3870 unsigned dynamicPos = 0;
3872 for (int64_t size : tileSizes) {
3873 if (size == ShapedType::kDynamic) {
3874 results.push_back(dynamic[dynamicPos++]);
3882SmallVector<OpFoldResult> transform::TileUsingForOp::getMixedInterchange() {
3886void transform::TileUsingForOp::getEffects(
3887 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
3901void transform::TileUsingForallOp::build(OpBuilder &builder,
3903 ArrayRef<int64_t> staticTileSizes,
3904 transform::TileSizesSpec,
3906 return build(builder,
result,
3914void transform::TileUsingForallOp::build(OpBuilder &builder,
3916 ArrayRef<OpFoldResult> mixedTileSizes,
3917 transform::TileSizesSpec,
3919 SmallVector<int64_t> staticTileSizes;
3920 SmallVector<Value> dynamicTileSizes;
3926 auto operationType = transform::AnyOpType::get(ctx);
3929 TypeRange{operationType, operationType},
3936 staticTileSizesAttr,
3940void transform::TileUsingForallOp::build(OpBuilder &builder,
3942 ArrayRef<int64_t> staticNumThreads,
3943 transform::NumThreadsSpec,
3947 NumThreadsSpec(), mapping);
3950void transform::TileUsingForallOp::build(OpBuilder &builder,
3952 ArrayRef<OpFoldResult> mixedNumThreads,
3953 transform::NumThreadsSpec,
3955 SmallVector<int64_t> staticNumThreads;
3956 SmallVector<Value> dynamicNumThreads;
3963 auto operationType = transform::AnyOpType::get(ctx);
3966 TypeRange{operationType, operationType},
3972 staticNumThreadsAttr,
3979static SmallVector<OpFoldResult>
3985 AffineExpr normalizedUbExpr = (s1 - s0).ceilDiv(s2);
3987 for (
auto [lb,
ub, step] : llvm::zip_equal(lbs, ubs, steps)) {
3989 rewriter, loc, normalizedUbExpr, {lb,
ub, step});
3990 normalizedUbs.push_back(normalizedUb);
3992 return normalizedUbs;
4008 for (
auto [iv, lb, step] : llvm::zip_equal(ivs, lbs, steps)) {
4011 denormalizedIvs.push_back(
4014 return denormalizedIvs;
4025 scf::ForallOp loop) {
4042 auto normalizedForallOp = scf::ForallOp::create(
4043 rewriter, loc, normalizedLbs, normalizedUbs, normalizedSteps,
4044 loop.getOutputs(), loop.getMapping(),
4047 auto normalizedLoopIvs = normalizedForallOp.getInductionVars();
4049 Block *normalizedLoopBlock = normalizedForallOp.getBody();
4054 argValues.append(normalizedForallOp.getRegionIterArgs().begin(),
4055 normalizedForallOp.getRegionIterArgs().end());
4056 Block *origLoopBlock = loop.getBody();
4057 rewriter.
mergeBlocks(origLoopBlock, normalizedLoopBlock, argValues);
4059 rewriter.
replaceOp(loop, normalizedForallOp);
4060 return normalizedForallOp;
4068 scf::SCFTilingResult &tilingResult) {
4070 auto tileableOp = dyn_cast<TilingInterface>(
target);
4073 transformOp.emitSilenceableError()
4074 <<
"only TilingInterface ops are supported";
4075 diag.attachNote(
target->getLoc()) <<
"target op";
4079 scf::SCFTilingOptions
options;
4080 options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
4081 if (!mixedNumThreads.empty()) {
4082 options.setNumThreads(mixedNumThreads);
4084 options.setTileSizes(mixedTileSizes);
4087 options.setMapping(mapping.value().getValue());
4089 FailureOr<scf::SCFTilingResult> maybeTilingResult =
4090 scf::tileUsingSCF(rewriter, tileableOp,
options);
4092 if (failed(maybeTilingResult))
4093 return transformOp.emitDefaultSilenceableFailure(tileableOp);
4095 rewriter.
replaceOp(tileableOp, maybeTilingResult->replacements);
4097 tilingResult = *maybeTilingResult;
4101 if (mixedNumThreads.empty() && !tilingResult.loops.empty()) {
4102 auto generatedForallOp = cast<scf::ForallOp>(tilingResult.loops.front());
4105 scf::ForallOp normalizedForallOp =
4107 tilingResult.loops.front() = normalizedForallOp;
4117 auto transformOp = cast<TransformOpInterface>(getOperation());
4126 getPackedNumThreads()
4128 state, transformOp, mixedNumThreads, getPackedNumThreads())
4130 state, transformOp, mixedNumThreads, getMixedNumThreads());
4134 status = getPackedTileSizes()
4136 state, transformOp, mixedTileSizes, getPackedTileSizes())
4138 state, transformOp, mixedTileSizes, getMixedTileSizes());
4143 scf::SCFTilingResult tilingResult;
4145 rewriter, state, transformOp,
target, mixedNumThreads, mixedTileSizes,
4146 getMapping(), tilingResult);
4147 if (!
diag.succeeded())
4149 if (!tilingResult.loops.empty())
4150 tileOps.push_back(tilingResult.loops.front());
4151 tiledOps.append(tilingResult.tiledOps);
4154 transformResults.
set(cast<OpResult>(getForallOp()), tileOps);
4155 transformResults.
set(cast<OpResult>(getTiledOp()), tiledOps);
4160void transform::TileUsingForallOp::getEffects(
4161 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
4171SmallVector<OpFoldResult> TileUsingForallOp::getMixedNumThreads() {
4176SmallVector<OpFoldResult> TileUsingForallOp::getMixedTileSizes() {
4181LogicalResult TileUsingForallOp::verify() {
4182 int numThreadsSpec =
static_cast<int>(!getMixedNumThreads().empty()) +
4183 static_cast<int>(getPackedNumThreads() != Value());
4184 if (numThreadsSpec > 1)
4186 "num_threads and packed_num_threads are mutually exclusive");
4187 int tileSizesSpec =
static_cast<int>(!getMixedTileSizes().empty()) +
4188 static_cast<int>(getPackedTileSizes() != Value());
4189 if (tileSizesSpec > 1)
4191 "tile_sizes and packed_tile_sizes are mutually exclusive");
4192 if (numThreadsSpec == 0 && tileSizesSpec == 0)
4193 return emitOpError(
"either (packed_)num_threads or (packed_)tile_sizes "
4194 "must be specified");
4202void transform::VectorizeChildrenAndApplyPatternsOp::build(
4203 OpBuilder &builder, OperationState &
result, Value
target,
4204 bool foldTypeExtensionsIntoContract,
bool vectorizePadding,
4205 bool vectorizeExtract,
bool flatten1DDepthwiseConv) {
4207 if (foldTypeExtensionsIntoContract) {
4209 VectorizeChildrenAndApplyPatternsOp::
4210 getFoldTypeExtensionsIntoContractAttrName(
result.name),
4213 if (vectorizePadding) {
4215 VectorizeChildrenAndApplyPatternsOp::getVectorizePaddingAttrName(
4219 if (vectorizeExtract) {
4221 VectorizeChildrenAndApplyPatternsOp::getVectorizeNdExtractAttrName(
4225 if (flatten1DDepthwiseConv) {
4227 VectorizeChildrenAndApplyPatternsOp::getFlatten_1dDepthwiseConvAttrName(
4237struct VectorizationPattern :
public RewritePattern {
4238 explicit VectorizationPattern(MLIRContext *context,
4239 bool vectorizeExtract =
false,
4240 bool flattenConv =
false)
4241 : RewritePattern(MatchAnyOpTypeTag(), 1, context),
4242 vectorizeNDExtract(vectorizeExtract),
4243 flatten1DDepthwiseConv(flattenConv) {}
4244 LogicalResult matchAndRewrite(Operation *op,
4245 PatternRewriter &rewriter)
const override {
4248 "Unsupported Op, cannot vectorize");
4249 FailureOr<VectorizationResult> vectorResults =
4251 {}, vectorizeNDExtract,
4252 flatten1DDepthwiseConv);
4253 if (
failed(vectorResults))
4255 rewriter.
replaceOp(op, vectorResults->replacements);
4262 bool vectorizeNDExtract =
false;
4266 bool flatten1DDepthwiseConv =
false;
4270DiagnosedSilenceableFailure
4271transform::VectorizeChildrenAndApplyPatternsOp::applyToOne(
4272 transform::TransformRewriter &rewriter, Operation *
target,
4273 transform::ApplyToEachResultList &results,
4274 transform::TransformState &state) {
4275 if (!
target->hasTrait<OpTrait::IsIsolatedFromAbove>()) {
4276 auto diag = this->
emitOpError(
"requires isolated-from-above targets");
4277 diag.attachNote(
target->getLoc()) <<
"non-isolated target";
4282 RewritePatternSet patterns(ctx);
4283 patterns.
add<VectorizationPattern>(ctx, getVectorizeNdExtract(),
4284 getFlatten_1dDepthwiseConv());
4286 if (!getDisableTransferPermutationMapLoweringPatterns())
4289 if (!getDisableMultiReductionToContractPatterns())
4294 patterns.
add<linalg::LinalgCopyVTRForwardingPattern,
4295 linalg::LinalgCopyVTWForwardingPattern>(ctx,
4297 vector::TransferReadOp::getCanonicalizationPatterns(patterns, ctx);
4298 vector::TransferWriteOp::getCanonicalizationPatterns(patterns, ctx);
4301 patterns.
add<CopyVectorizationPattern>(ctx);
4303 if (getFoldTypeExtensionsIntoContract())
4306 if (getVectorizePadding()) {
4314 TrackingListener listener(state, *
this);
4317 GreedyRewriteConfig().setListener(&listener))))
4318 return emitDefaultDefiniteFailure(
target);
4328DiagnosedSilenceableFailure transform::VectorizeOp::apply(
4329 transform::TransformRewriter &rewriter,
4330 mlir::transform::TransformResults &transformResults,
4331 mlir::transform::TransformState &state) {
4333 if (std::empty(targets))
4335 auto transformOp = cast<TransformOpInterface>(getOperation());
4336 SmallVector<int64_t> vectorSizes;
4338 state, transformOp, getMixedVectorSizes(), vectorSizes);
4343 for (Operation *
target : targets) {
4346 <<
"Unsupported Op, cannot vectorize";
4348 FailureOr<VectorizationResult> vectorResults =
4350 getVectorizeNdExtract().value_or(
false),
4352 getAssumeDynamicDimsMatchVecSizes().value_or(
false),
4353 getCreateNamedContraction().value_or(
false));
4354 if (
failed(vectorResults)) {
4356 <<
"Attempted to vectorize, but failed";
4364void transform::VectorizeOp::getEffects(
4365 SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
4371SmallVector<OpFoldResult> VectorizeOp::getMixedVectorSizes() {
4376LogicalResult transform::VectorizeOp::verify() {
4377 if (getStaticVectorSizes().size() != getScalableSizes().size())
4378 return emitOpError(
"expected same number of vector sizes (")
4379 << getStaticVectorSizes().size() <<
") and scalable sizes ("
4380 << getScalableSizes().size() <<
")";
4388DiagnosedSilenceableFailure
4389transform::HoistRedundantVectorTransfersOp::applyToOne(
4390 transform::TransformRewriter &rewriter, func::FuncOp
target,
4391 transform::ApplyToEachResultList &results,
4392 transform::TransformState &state) {
4405DiagnosedSilenceableFailure
4406transform::HoistRedundantVectorBroadcastsOp::applyToOne(
4407 transform::TransformRewriter &rewriter, mlir::Operation *
target,
4408 transform::ApplyToEachResultList &results,
4409 transform::TransformState &state) {
4420DiagnosedSilenceableFailure transform::ConvertConv2DToImg2ColOp::applyToOne(
4421 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4422 transform::ApplyToEachResultList &results,
4423 transform::TransformState &state) {
4425 auto maybeTransformed =
4428 .Case([&](linalg::Conv2DNhwcHwcfOp op) {
4431 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4434 .Case([&](linalg::DepthwiseConv2DNhwcHwcOp op) {
4437 .Case([&](linalg::Conv2DNchwFchwOp op) {
4440 .Default([&](Operation *op) {
4443 if (
failed(maybeTransformed))
4444 return emitDefaultSilenceableFailure(
target);
4446 results.
push_back(maybeTransformed->first);
4448 results.
push_back(maybeTransformed->second);
4456DiagnosedSilenceableFailure transform::FlattenElementwiseLinalgOp::applyToOne(
4457 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4458 transform::ApplyToEachResultList &results,
4459 transform::TransformState &state) {
4463 <<
"only elementwise flattening is supported";
4465 if (!llvm::all_of(
target.getIndexingMapsArray(), [](AffineMap m) {
4466 return m.isPermutation() || m.getNumResults() == 0;
4470 <<
"broadcasting of non scalar operands is not supported";
4474 if (
target.getNumLoops() <= 1) {
4481 if (
auto broadcastOp = dyn_cast<linalg::BroadcastOp>(
target.getOperation());
4482 broadcastOp && broadcastOp.getInput().getType().getRank() != 0) {
4489 std::iota(reassociation.begin(), reassociation.end(), 0);
4490 auto maybeFlattened =
4492 if (
failed(maybeFlattened))
4494 <<
"attempted to flatten, but failed";
4495 results.
push_back(maybeFlattened->collapsedOp);
4504DiagnosedSilenceableFailure transform::TransposeConv2DOp::applyToOne(
4505 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4506 transform::ApplyToEachResultList &results,
4507 transform::TransformState &state) {
4509 auto maybeTransformed =
4511 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4514 .Case([&](linalg::Conv2DNhwcFhwcQOp op) {
4517 .Default([&](Operation *op) {
4520 if (
failed(maybeTransformed))
4521 return emitDefaultSilenceableFailure(
target);
4531DiagnosedSilenceableFailure transform::TransposeMatmulOp::applyToOne(
4532 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4533 transform::ApplyToEachResultList &results,
4534 transform::TransformState &state) {
4536 bool transposeLHS = getInputToTranspose() == TransposeMatmulInput::lhs;
4537 auto maybeTransformed =
4539 .Case([&](linalg::MatmulOp op) {
4542 .Case([&](linalg::BatchMatmulOp op) {
4545 .Default(failure());
4546 if (
failed(maybeTransformed))
4556template <
typename OpTy>
4557static DiagnosedSilenceableFailure
4561 static_assert(llvm::is_one_of<OpTy, tensor::InsertSliceOp,
4562 tensor::ParallelInsertSliceOp>() &&
4565 if (
auto copySource =
4566 target.getSource().template getDefiningOp<linalg::CopyOp>()) {
4574 if (isa<mlir::ParallelCombiningOpInterface>(
target.getOperation()))
4577 Value extracted = tensor::ExtractSliceOp::create(
4580 Value copied = linalg::CopyOp::create(rewriter,
target.getLoc(),
4581 target.getSource(), extracted)
4593DiagnosedSilenceableFailure transform::InsertSliceToCopyOp::applyToOne(
4594 transform::TransformRewriter &rewriter, Operation *targetOp,
4595 transform::ApplyToEachResultList &results,
4596 transform::TransformState &state) {
4599 if (
auto target = dyn_cast<tensor::InsertSliceOp>(targetOp))
4600 return doit(rewriter,
target, results, state);
4601 if (
auto target = dyn_cast<tensor::ParallelInsertSliceOp>(targetOp))
4602 return doit(rewriter,
target, results, state);
4604 DiagnosedSilenceableFailure
diag =
4605 emitSilenceableError()
4606 <<
"only InsertSliceOp and ParallelInsertSliceOp ops are supported";
4607 diag.attachNote(targetOp->
getLoc()) <<
"target op";
4615DiagnosedSilenceableFailure transform::MapCopyToThreadsOp::applyToOne(
4616 transform::TransformRewriter &rewriter, Operation *
target,
4617 transform::ApplyToEachResultList &results,
4618 transform::TransformState &state) {
4620 if (!isa<linalg::CopyOp, tensor::PadOp>(
target)) {
4621 DiagnosedSilenceableFailure
diag =
4622 emitSilenceableError()
4623 <<
"only linalg.copy and tensor.pad target ops are supported";
4624 diag.attachNote(
target->getLoc()) <<
"target op";
4627 assert(
target->getNumResults() == 1 &&
"expected single result");
4628 auto resultShapedType = cast<ShapedType>(
target->getResult(0).getType());
4629 if (!resultShapedType.hasStaticShape()) {
4630 DiagnosedSilenceableFailure
diag =
4631 emitSilenceableError()
4632 <<
"only statically sized ops of rank <= 3 are supported";
4633 diag.attachNote(
target->getLoc()) <<
"target op";
4638 int64_t desiredBitAlignment = getDesiredBitAlignment();
4639 int64_t eltBitwidth =
4640 resultShapedType.getElementType().getIntOrFloatBitWidth();
4641 if (desiredBitAlignment % eltBitwidth != 0) {
4642 desiredBitAlignment = eltBitwidth;
4645 gpu::CopyMappingInfo mapping(
4647 getTotalNumThreads(),
4648 desiredBitAlignment,
4649 resultShapedType.getShape(),
4652 resultShapedType.getElementType().getIntOrFloatBitWidth());
4653 if (mapping.status == gpu::CopyMappingInfo::Status::Invalid) {
4654 DiagnosedSilenceableFailure
diag =
4655 emitSilenceableError()
4656 <<
"too few threads to map copy op to threads on the most minor "
4657 "dimension, given alignment and vector size constraints, try "
4658 "smaller tile size of mapping to more threads";
4659 diag.attachNote(
target->getLoc()) <<
"target op";
4665 scf::SCFTilingResult tilingResult;
4672 ArrayRef<OpFoldResult>{},
4673 b.getArrayAttr(mapping.threadMapping),
4675 if (!
diag.succeeded())
4678 results.
push_back(tilingResult.loops.front());
4679 for (
auto *op : tilingResult.tiledOps)
4688DiagnosedSilenceableFailure transform::WinogradConv2DOp::applyToOne(
4689 transform::TransformRewriter &rewriter, linalg::LinalgOp
target,
4690 transform::ApplyToEachResultList &results,
4691 transform::TransformState &state) {
4693 FailureOr<Operation *> maybeTransformed = failure();
4695 .Case([&](linalg::Conv2DNhwcFhwcOp op) {
4700 .Default([&](Operation *op) {
return false; });
4703 return emitSilenceableError()
4704 <<
"this operation is not supported to convert to Winograd Conv2D";
4707 if (
failed(maybeTransformed)) {
4708 return emitSilenceableError() <<
"apply Winograd Conv2D failed";
4715DiagnosedSilenceableFailure transform::DecomposeWinogradOp::applyToOne(
4716 transform::TransformRewriter &rewriter, Operation *
target,
4717 transform::ApplyToEachResultList &results,
4718 transform::TransformState &state) {
4720 FailureOr<Operation *> maybeTransformed = failure();
4723 .Case([&](linalg::WinogradFilterTransformOp op) {
4727 .Case([&](linalg::WinogradInputTransformOp op) {
4731 .Case([&](linalg::WinogradOutputTransformOp op) {
4738 DiagnosedSilenceableFailure
diag =
4739 emitSilenceableError()
4740 <<
"this operation is not supported to decompose into other operations";
4741 diag.attachNote(
target->getLoc()) <<
"target op";
4745 if (
failed(maybeTransformed)) {
4746 DiagnosedSilenceableFailure
diag =
4747 emitSilenceableError() <<
"decompose Winograd operations failed";
4748 diag.attachNote(
target->getLoc()) <<
"target op";
4756#include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOpsEnums.cpp.inc"
4758#define GET_OP_CLASSES
4759#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.
Attribute getDiscardableAttr(StringRef name)
Access a discardable attribute by name, returns a null Attribute if the discardable attribute does no...
OpResult getOpResult(unsigned idx)
void setOperand(unsigned idx, Value value)
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
std::optional< Attribute > getInherentAttr(StringRef name)
Access an inherent attribute by name: returns an empty optional if there is no inherent attribute wit...
Location getLoc()
The source location the operation was defined or derived from.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
operand_type_range getOperandTypes()
result_type_range getResultTypes()
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
user_range getUsers()
Returns a range of all users.
result_range getOpResults()
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
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.
void populateSwapExtractSliceWithFillPatterns(RewritePatternSet &patterns)
Adds patterns that waps tensor.extract_slice(linalg.fill(cst, init)) into linalg.fill(cst,...
FailureOr< LinalgOp > promoteSubViews(OpBuilder &b, LinalgOp op, const LinalgPromotionOptions &options)
Promote the subViews into a new buffer allocated at the insertion point b.
LogicalResult deallocateWorkgroupMemory(OpBuilder &, Value)
In case of GPU group memory there is no need to deallocate.
FailureOr< Operation * > transposeMatmul(RewriterBase &rewriter, linalg::MatmulOp op, bool transposeLHS=true)
Convert Linalg matmul ops to transposed variants.
FailureOr< CollapseResult > collapseOpIterationDims(LinalgOp op, ArrayRef< ReassociationIndices > foldedIterationDims, RewriterBase &rewriter)
Collapses dimensions of linalg.generic/linalg.copy operation.
void hoistRedundantVectorTransfers(Operation *root, bool verifyNonZeroTrip=false)
Hoist vector.transfer_read/vector.transfer_write on buffers pairs out of immediately enclosing scf::F...
FailureOr< Operation * > decomposeWinogradInputTransformOp(RewriterBase &rewriter, linalg::WinogradInputTransformOp op)
Rewrite linalg.winograd_input_transform.
void populateDecomposePadPatterns(RewritePatternSet &patterns)
Populates patterns to decompose tensor.pad into e.g.
void populateFoldAddIntoDestPatterns(RewritePatternSet &patterns)
Pattern to replace linalg.add when destination passing on a contraction op suffices for achieving the...
std::pair< TilingInterface, TilingInterface > splitOp(RewriterBase &rewriter, TilingInterface op, unsigned dimension, OpFoldResult splitPoint)
Split the given op into two parts along the given iteration space dimension at the specified splitPoi...
FailureOr< SplitReductionResult > splitReductionByScaling(RewriterBase &b, LinalgOp op, const ControlSplitReductionFn &controlSplitReductionFn, bool useAlloc=false)
Scaling-based implementation of the split reduction transformation.
FailureOr< MultiSizeSpecification > computeMultiTileSizes(OpBuilder &builder, LinalgOp op, unsigned dimension, OpFoldResult targetSize, OpFoldResult divisor, bool emitAssertions=true)
Emits the IR computing the multi-sized tiling specification with two tile sizes not exceeding targetS...
FailureOr< LinalgOp > downscaleSizeOneWindowedConvolution(RewriterBase &rewriter, LinalgOp op)
Rewrite convolution/pooling/depthwise ops with size-1 window dimensions into lower-dimensional ops.
FailureOr< LowerPackResult > lowerPack(RewriterBase &rewriter, linalg::PackOp packOp, bool lowerPadLikeWithInsertSlice=true)
Rewrite pack as pad + reshape + transpose.
ForOp getForInductionVarOwner(Value val)
Returns the loop parent of an induction variable.
void populateMergeConsecutiveInsertExtractSlicePatterns(RewritePatternSet &patterns)
Collects patterns to merge consecutive tensor.insert_slice/extract_slice into one.
void populateBubbleUpExtractSliceOpPatterns(RewritePatternSet &patterns)
Appends patterns that are used to bubble up tensor.extract slice op above its producer.
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given tensor value.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
LogicalResult getOrCreateDestinations(OpBuilder &b, Location loc, Operation *op, SmallVector< Value > &result)
This is a helper function for DestinationStyleOpInterface.
void populateFoldTensorSubsetIntoVectorTransferPatterns(RewritePatternSet &patterns)
Appends patterns for folding tensor subset ops into vector transfer ops.
detail::poison_attr_matcher m_Poison()
Matches a poison constant (any attribute implementing PoisonAttrInterface).
void populateVectorTransferPermutationMapLoweringPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect a set of transfer read/write lowering patterns that simplify the permutation map (e....
void populateFoldArithExtensionPatterns(RewritePatternSet &patterns)
Collect a set of patterns that fold arithmetic extension on floating point into vector contract for t...
void populateSinkVectorOpsPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Patterns that remove redundant Vector Ops by re-ordering them with e.g.
void populateVectorReductionToContractPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect patterns to convert reduction op to vector.contract and fold transpose/broadcast ops into the...
void populateVectorStepLoweringPatterns(RewritePatternSet &patterns, unsigned indexBitwidth=64, PatternBenefit benefit=1)
Populate the pattern set with the following patterns:
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
@ PartialReductionOuterReduction
@ PartialReductionOuterParallel
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
SmallVector< InnerTileAlignment > convertInnerTileAlignments(ArrayRef< int64_t > alignments)
Maps a validated inner_tile_alignments integer array onto the per-dimension InnerTileAlignment hints ...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
DiagnosedSilenceableFailure emitSilenceableFailure(Location loc, const Twine &message={})
Emits a silenceable failure with the given message.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Attribute parseAttribute(llvm::StringRef attrStr, MLIRContext *context, Type type={}, size_t *numRead=nullptr, bool isKnownNullTerminated=false)
This parses a single MLIR attribute to an MLIR context if it was valid.
llvm::SetVector< T, Vector, Set, N > SetVector
DiagnosedDefiniteFailure emitDefiniteFailure(Location loc, const Twine &message={})
Emits a definite failure with the given message.
LogicalResult verifyInnerTileAlignments(Operation *op, ArrayRef< int64_t > alignments)
Verifies that every entry of a raw inner_tile_alignments integer array is a valid InnerTileAlignment,...
FailureOr< SCFTileAndFuseResult > tileConsumerAndFuseProducersUsingSCF(RewriterBase &rewriter, TilingInterface consumer, const SCFTileAndFuseOptions &options)
Method to tile and fuse a sequence of operations, by tiling the consumer and fusing its producers.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
llvm::TypeSwitch< T, ResultT > TypeSwitch
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
SmallVector< int64_t, 2 > ReassociationIndices
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
SmallVector< IntTy > extractFromIntegerArrayAttr(Attribute attr)
Extract integer values from the assumed ArrayAttr of IntegerAttr.
llvm::function_ref< Fn > function_ref
bool isPermutationVector(ArrayRef< int64_t > interchange)
Method to check if an interchange vector is a permutation.
bool isOneInteger(OpFoldResult v)
Return true if v is an IntegerAttr with value 1.
FailureOr< SCFTilingResult > tileUsingSCF(RewriterBase &rewriter, TilingInterface op, const SCFTilingOptions &options)
Method to tile an op that implements the TilingInterface using scf.for for iterating over the tiles.
This class represents a listener that may be used to hook into various actions within an OpBuilder.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Represents a range (offset, size, and stride) where each element of the triple may be dynamic or stat...
A listener that forwards all notifications to another listener.
ForwardingListener(OpBuilder::Listener *listener)
Container for result values of tiling.
SmallVector< Value > tiledValues
Options for analysis-enabled bufferization.
Transformation to drop unit-extent dimensions from linalg.generic operations.