13c2a74a3SMatthias Springer //===- LinalgTransformOps.cpp - Implementation of Linalg transform ops ----===//
23c2a74a3SMatthias Springer //
33c2a74a3SMatthias Springer // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43c2a74a3SMatthias Springer // See https://llvm.org/LICENSE.txt for license information.
53c2a74a3SMatthias Springer // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63c2a74a3SMatthias Springer //
73c2a74a3SMatthias Springer //===----------------------------------------------------------------------===//
83c2a74a3SMatthias Springer 
93c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.h"
103c2a74a3SMatthias Springer 
113c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/IR/Linalg.h"
123c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/Transforms/Transforms.h"
133c2a74a3SMatthias Springer #include "mlir/Dialect/PDL/IR/PDL.h"
143c2a74a3SMatthias Springer #include "mlir/Dialect/PDL/IR/PDLTypes.h"
153c2a74a3SMatthias Springer #include "mlir/Dialect/Transform/IR/TransformDialect.h"
163c2a74a3SMatthias Springer #include "mlir/Interfaces/SideEffectInterfaces.h"
173c2a74a3SMatthias Springer #include "mlir/Parser/Parser.h"
183c2a74a3SMatthias Springer #include "llvm/Support/FormatVariadic.h"
193c2a74a3SMatthias Springer 
203c2a74a3SMatthias Springer using namespace mlir;
213c2a74a3SMatthias Springer using namespace mlir::linalg;
223c2a74a3SMatthias Springer using namespace mlir::transform;
233c2a74a3SMatthias Springer 
243c2a74a3SMatthias Springer /// Extracts a vector of int64_t from an array attribute. Asserts if the
253c2a74a3SMatthias Springer /// attribute contains values other than integers.
263c2a74a3SMatthias Springer static SmallVector<int64_t> extractI64Array(ArrayAttr attr) {
273c2a74a3SMatthias Springer   SmallVector<int64_t> result;
283c2a74a3SMatthias Springer   result.reserve(attr.size());
293c2a74a3SMatthias Springer   for (APInt value : attr.getAsValueRange<IntegerAttr>())
303c2a74a3SMatthias Springer     result.push_back(value.getSExtValue());
313c2a74a3SMatthias Springer   return result;
323c2a74a3SMatthias Springer }
333c2a74a3SMatthias Springer 
343c2a74a3SMatthias Springer /// Extracts a vector of unsigned from an array attribute. Asserts if the
353c2a74a3SMatthias Springer /// attribute contains values other than intergers. May truncate.
363c2a74a3SMatthias Springer static SmallVector<unsigned> extractUIntArray(ArrayAttr attr) {
373c2a74a3SMatthias Springer   SmallVector<unsigned> result;
383c2a74a3SMatthias Springer   result.reserve(attr.size());
393c2a74a3SMatthias Springer   for (APInt value : attr.getAsValueRange<IntegerAttr>())
403c2a74a3SMatthias Springer     result.push_back(value.getZExtValue());
413c2a74a3SMatthias Springer   return result;
423c2a74a3SMatthias Springer }
433c2a74a3SMatthias Springer 
443c2a74a3SMatthias Springer namespace {
453c2a74a3SMatthias Springer /// A simple pattern rewriter that implements no special logic.
463c2a74a3SMatthias Springer class SimpleRewriter : public PatternRewriter {
473c2a74a3SMatthias Springer public:
483c2a74a3SMatthias Springer   SimpleRewriter(MLIRContext *context) : PatternRewriter(context) {}
493c2a74a3SMatthias Springer };
503c2a74a3SMatthias Springer } // namespace
513c2a74a3SMatthias Springer 
523c2a74a3SMatthias Springer //===----------------------------------------------------------------------===//
533c2a74a3SMatthias Springer // TileOp
543c2a74a3SMatthias Springer //===----------------------------------------------------------------------===//
553c2a74a3SMatthias Springer 
563c2a74a3SMatthias Springer /// Apply a tiling transformation to all payload ops and store both the
573c2a74a3SMatthias Springer /// tiled operation as well as the created tile loops.
583c2a74a3SMatthias Springer static LogicalResult
593c2a74a3SMatthias Springer applyTilingToAll(Operation *transformOp, Value target,
603c2a74a3SMatthias Springer                  ArrayRef<int64_t> tileSizes,
613c2a74a3SMatthias Springer                  transform::TransformResults &transformResults,
623c2a74a3SMatthias Springer                  transform::TransformState &state,
633c2a74a3SMatthias Springer                  function_ref<FailureOr<TiledLinalgOp>(LinalgOp)> applyFn) {
643c2a74a3SMatthias Springer   // Number of loops: Number of tiles sizes that are not zero.
653c2a74a3SMatthias Springer   size_t numLoops = tileSizes.size() - llvm::count(tileSizes, 0);
663c2a74a3SMatthias Springer   // All payload ops. These should all be LinalgOps for now.
673c2a74a3SMatthias Springer   ArrayRef<Operation *> payloadOps = state.getPayloadOps(target);
683c2a74a3SMatthias Springer 
693c2a74a3SMatthias Springer   SmallVector<Operation *> tiledLinalgOps;
703c2a74a3SMatthias Springer   SmallVector<SmallVector<Operation *>> loopOps(numLoops);
713c2a74a3SMatthias Springer   for (unsigned int i = 0; i < numLoops; ++i)
723c2a74a3SMatthias Springer     loopOps[i].reserve(payloadOps.size());
733c2a74a3SMatthias Springer 
743c2a74a3SMatthias Springer   for (Operation *target : payloadOps) {
753c2a74a3SMatthias Springer     auto linalgOp = dyn_cast<linalg::LinalgOp>(target);
763c2a74a3SMatthias Springer     if (!linalgOp)
773c2a74a3SMatthias Springer       return transformOp->emitError("only LinalgOps are supported");
783c2a74a3SMatthias Springer 
793c2a74a3SMatthias Springer     FailureOr<TiledLinalgOp> tiled = applyFn(linalgOp);
803c2a74a3SMatthias Springer     if (failed(tiled))
813c2a74a3SMatthias Springer       return failure();
823c2a74a3SMatthias Springer 
833c2a74a3SMatthias Springer     tiledLinalgOps.push_back(tiled->op);
843c2a74a3SMatthias Springer     if (tiled->loops.size() != numLoops)
853c2a74a3SMatthias Springer       // Not enough loops were generated. This usually means that the input size
863c2a74a3SMatthias Springer       // was smaller than the tiling size.
873c2a74a3SMatthias Springer       // TODO: LinalgTilingPattern should return failure().
883c2a74a3SMatthias Springer       return failure();
893c2a74a3SMatthias Springer     for (unsigned int i = 0; i < numLoops; ++i)
903c2a74a3SMatthias Springer       loopOps[i].push_back(tiled->loops[i]);
913c2a74a3SMatthias Springer   }
923c2a74a3SMatthias Springer 
933c2a74a3SMatthias Springer   transformResults.set(transformOp->getOpResult(0), tiledLinalgOps);
943c2a74a3SMatthias Springer   for (unsigned int i = 0; i < numLoops; ++i)
953c2a74a3SMatthias Springer     transformResults.set(transformOp->getOpResult(i + 1), loopOps[i]);
963c2a74a3SMatthias Springer   return success();
973c2a74a3SMatthias Springer }
983c2a74a3SMatthias Springer 
993c2a74a3SMatthias Springer LogicalResult transform::TileOp::apply(TransformResults &transformResults,
1003c2a74a3SMatthias Springer                                        TransformState &state) {
1013c2a74a3SMatthias Springer   LinalgTilingOptions tilingOptions;
1023c2a74a3SMatthias Springer   SmallVector<int64_t> tileSizes = extractI64Array(getSizes());
1033c2a74a3SMatthias Springer 
1043c2a74a3SMatthias Springer   if (!tileSizes.empty())
1053c2a74a3SMatthias Springer     tilingOptions.setTileSizes(tileSizes);
1063c2a74a3SMatthias Springer   tilingOptions.setInterchange(extractUIntArray(getInterchange()));
1073c2a74a3SMatthias Springer   LinalgTilingPattern pattern(getContext(), tilingOptions);
1083c2a74a3SMatthias Springer 
1093c2a74a3SMatthias Springer   return applyTilingToAll(getOperation(), getTarget(), tileSizes,
1103c2a74a3SMatthias Springer                           transformResults, state, [&](LinalgOp linalgOp) {
1113c2a74a3SMatthias Springer                             SimpleRewriter rewriter(linalgOp.getContext());
1123c2a74a3SMatthias Springer                             return pattern.returningMatchAndRewrite(linalgOp,
1133c2a74a3SMatthias Springer                                                                     rewriter);
1143c2a74a3SMatthias Springer                           });
1153c2a74a3SMatthias Springer }
1163c2a74a3SMatthias Springer 
1173c2a74a3SMatthias Springer ParseResult transform::TileOp::parse(OpAsmParser &parser,
1183c2a74a3SMatthias Springer                                      OperationState &result) {
1193c2a74a3SMatthias Springer   StringRef sizesAttrName = TileOp::getSizesAttrName(result.name).getValue();
1203c2a74a3SMatthias Springer   OpAsmParser::UnresolvedOperand targetOperand;
121*1d7b5cd5SChris Lattner   SMLoc opLoc = parser.getCurrentLocation();
122*1d7b5cd5SChris Lattner   if (parser.parseOperand(targetOperand) ||
123*1d7b5cd5SChris Lattner       parser.parseOptionalAttrDict(result.attributes))
1243c2a74a3SMatthias Springer     return failure();
1253c2a74a3SMatthias Springer   Attribute sizesAttr = result.attributes.get(sizesAttrName);
1263c2a74a3SMatthias Springer   if (!sizesAttr)
1273c2a74a3SMatthias Springer     return parser.emitError(opLoc)
1283c2a74a3SMatthias Springer            << "expected '" << sizesAttrName << "' attribute";
1293c2a74a3SMatthias Springer   auto sizesArrayAttr = sizesAttr.dyn_cast<ArrayAttr>();
1303c2a74a3SMatthias Springer   if (!sizesArrayAttr)
1313c2a74a3SMatthias Springer     return parser.emitError(opLoc)
1323c2a74a3SMatthias Springer            << "'" << sizesAttrName << "' attribute must be an array";
1333c2a74a3SMatthias Springer   Type pdlOpType = parser.getBuilder().getType<pdl::OperationType>();
1343c2a74a3SMatthias Springer   size_t numExpectedLoops =
1353c2a74a3SMatthias Springer       sizesArrayAttr.size() - llvm::count(extractI64Array(sizesArrayAttr), 0);
1363c2a74a3SMatthias Springer   result.addTypes(SmallVector<Type>(numExpectedLoops + 1, pdlOpType));
1373c2a74a3SMatthias Springer   if (parser.resolveOperand(targetOperand, pdlOpType, result.operands))
1383c2a74a3SMatthias Springer     return failure();
1393c2a74a3SMatthias Springer   return success();
1403c2a74a3SMatthias Springer }
1413c2a74a3SMatthias Springer 
1423c2a74a3SMatthias Springer void TileOp::print(OpAsmPrinter &p) {
1433c2a74a3SMatthias Springer   p << ' ';
1443c2a74a3SMatthias Springer   p << getTarget();
1453c2a74a3SMatthias Springer   p.printOptionalAttrDict((*this)->getAttrs());
1463c2a74a3SMatthias Springer }
1473c2a74a3SMatthias Springer 
1483c2a74a3SMatthias Springer void TileOp::getEffects(
1493c2a74a3SMatthias Springer     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1503c2a74a3SMatthias Springer         &effects) {
1513c2a74a3SMatthias Springer   // `target` arg is consumed and can no longer be used.
1523c2a74a3SMatthias Springer   effects.emplace_back(MemoryEffects::Read::get(), getTarget(),
1533c2a74a3SMatthias Springer                        TransformMappingResource::get());
1543c2a74a3SMatthias Springer   effects.emplace_back(MemoryEffects::Free::get(), getTarget(),
1553c2a74a3SMatthias Springer                        TransformMappingResource::get());
1563c2a74a3SMatthias Springer 
1573c2a74a3SMatthias Springer   for (Value r : getResults()) {
1583c2a74a3SMatthias Springer     effects.emplace_back(MemoryEffects::Write::get(), r,
1593c2a74a3SMatthias Springer                          TransformMappingResource::get());
1603c2a74a3SMatthias Springer     effects.emplace_back(MemoryEffects::Allocate::get(), r,
1613c2a74a3SMatthias Springer                          TransformMappingResource::get());
1623c2a74a3SMatthias Springer   }
1633c2a74a3SMatthias Springer 
1643c2a74a3SMatthias Springer   effects.emplace_back(MemoryEffects::Read::get(), PayloadIRResource::get());
1653c2a74a3SMatthias Springer   effects.emplace_back(MemoryEffects::Write::get(), PayloadIRResource::get());
1663c2a74a3SMatthias Springer }
1673c2a74a3SMatthias Springer 
1683c2a74a3SMatthias Springer //===----------------------------------------------------------------------===//
1693c2a74a3SMatthias Springer // Transform op registration
1703c2a74a3SMatthias Springer //===----------------------------------------------------------------------===//
1713c2a74a3SMatthias Springer 
1723c2a74a3SMatthias Springer namespace {
1733c2a74a3SMatthias Springer /// Registers new ops and declares PDL as dependent dialect since the additional
1743c2a74a3SMatthias Springer /// ops are using PDL types for operands and results.
1753c2a74a3SMatthias Springer class LinalgTransformDialectExtension
1763c2a74a3SMatthias Springer     : public transform::TransformDialectExtension<
1773c2a74a3SMatthias Springer           LinalgTransformDialectExtension> {
1783c2a74a3SMatthias Springer public:
1793c2a74a3SMatthias Springer   LinalgTransformDialectExtension() {
1803c2a74a3SMatthias Springer     declareDependentDialect<pdl::PDLDialect>();
1813c2a74a3SMatthias Springer     declareDependentDialect<scf::SCFDialect>();
1823c2a74a3SMatthias Springer     registerTransformOps<
1833c2a74a3SMatthias Springer #define GET_OP_LIST
1843c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp.inc"
1853c2a74a3SMatthias Springer         >();
1863c2a74a3SMatthias Springer   }
1873c2a74a3SMatthias Springer };
1883c2a74a3SMatthias Springer } // namespace
1893c2a74a3SMatthias Springer 
1903c2a74a3SMatthias Springer #define GET_OP_CLASSES
1913c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp.inc"
1923c2a74a3SMatthias Springer 
1933c2a74a3SMatthias Springer void mlir::linalg::registerTransformDialectExtension(
1943c2a74a3SMatthias Springer     DialectRegistry &registry) {
1953c2a74a3SMatthias Springer   registry.addExtensions<LinalgTransformDialectExtension>();
1963c2a74a3SMatthias Springer }
197