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 113963b4d0SAlex Zinenko #include "mlir/Dialect/Affine/IR/AffineOps.h" 124e4a4c05SAlex Zinenko #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h" 133c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/IR/Linalg.h" 143c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/Transforms/Transforms.h" 153c2a74a3SMatthias Springer #include "mlir/Dialect/PDL/IR/PDL.h" 163c2a74a3SMatthias Springer #include "mlir/Dialect/PDL/IR/PDLTypes.h" 173c2a74a3SMatthias Springer #include "mlir/Dialect/Transform/IR/TransformDialect.h" 183963b4d0SAlex Zinenko #include "mlir/Dialect/Transform/IR/TransformInterfaces.h" 1918b92c66SNicolas Vasilache #include "mlir/Interfaces/TilingInterface.h" 203c2a74a3SMatthias Springer #include "mlir/Parser/Parser.h" 21cc6c1592SAlex Zinenko #include "mlir/Transforms/GreedyPatternRewriteDriver.h" 221f77f01cSNicolas Vasilache #include "llvm/ADT/StringSet.h" 233c2a74a3SMatthias Springer 243c2a74a3SMatthias Springer using namespace mlir; 253c2a74a3SMatthias Springer using namespace mlir::linalg; 263c2a74a3SMatthias Springer using namespace mlir::transform; 273c2a74a3SMatthias Springer 283c2a74a3SMatthias Springer /// Extracts a vector of unsigned from an array attribute. Asserts if the 293c2a74a3SMatthias Springer /// attribute contains values other than intergers. May truncate. 303c2a74a3SMatthias Springer static SmallVector<unsigned> extractUIntArray(ArrayAttr attr) { 313c2a74a3SMatthias Springer SmallVector<unsigned> result; 323c2a74a3SMatthias Springer result.reserve(attr.size()); 333c2a74a3SMatthias Springer for (APInt value : attr.getAsValueRange<IntegerAttr>()) 343c2a74a3SMatthias Springer result.push_back(value.getZExtValue()); 353c2a74a3SMatthias Springer return result; 363c2a74a3SMatthias Springer } 373c2a74a3SMatthias Springer 383c2a74a3SMatthias Springer namespace { 393c2a74a3SMatthias Springer /// A simple pattern rewriter that implements no special logic. 403c2a74a3SMatthias Springer class SimpleRewriter : public PatternRewriter { 413c2a74a3SMatthias Springer public: 423c2a74a3SMatthias Springer SimpleRewriter(MLIRContext *context) : PatternRewriter(context) {} 433c2a74a3SMatthias Springer }; 443c2a74a3SMatthias Springer } // namespace 453c2a74a3SMatthias Springer 46ce2e198bSAlex Zinenko /// Attempts to apply the pattern specified as template argument to the given 47ce2e198bSAlex Zinenko /// operation. The pattern is expected to have a `returningMatchAndRewrite` 48ce2e198bSAlex Zinenko /// function that returns the "main" result or failure. Returns failure if the 49ce2e198bSAlex Zinenko /// pattern failed to apply. Extra arguments are forwarded to the pattern 50ce2e198bSAlex Zinenko /// constructor. 51ce2e198bSAlex Zinenko template <typename PatternTy, typename... Args> 52ce2e198bSAlex Zinenko static FailureOr<LinalgOp> tryApply(Operation *operation, Args &&...args) { 53ce2e198bSAlex Zinenko // Check if the given operation has the type expected by the pattern. 54ce2e198bSAlex Zinenko using OpTy = typename llvm::function_traits< 55ce2e198bSAlex Zinenko decltype(&PatternTy::returningMatchAndRewrite)>::template arg_t<0>; 56ce2e198bSAlex Zinenko auto op = dyn_cast<OpTy>(operation); 57ce2e198bSAlex Zinenko if (!op) 58ce2e198bSAlex Zinenko return failure(); 59ce2e198bSAlex Zinenko 60ce2e198bSAlex Zinenko // Apply the pattern directly to the op. 61ce2e198bSAlex Zinenko PatternTy pattern(operation->getContext(), std::forward<Args>(args)...); 62ce2e198bSAlex Zinenko SimpleRewriter rewriter(operation->getContext()); 63ce2e198bSAlex Zinenko rewriter.setInsertionPoint(operation); 64ce2e198bSAlex Zinenko auto result = pattern.returningMatchAndRewrite(op, rewriter); 65ce2e198bSAlex Zinenko if (failed(result)) 66ce2e198bSAlex Zinenko return failure(); 67ce2e198bSAlex Zinenko return cast<LinalgOp>(result->getOperation()); 68ce2e198bSAlex Zinenko } 69ce2e198bSAlex Zinenko 70ce2e198bSAlex Zinenko //===----------------------------------------------------------------------===// 71ce2e198bSAlex Zinenko // DecomposeOp 72ce2e198bSAlex Zinenko //===----------------------------------------------------------------------===// 73ce2e198bSAlex Zinenko 7452307109SNicolas Vasilache DiagnosedSilenceableFailure 7552307109SNicolas Vasilache transform::DecomposeOp::applyToOne(linalg::LinalgOp target, 7652307109SNicolas Vasilache SmallVectorImpl<Operation *> &results, 7752307109SNicolas Vasilache transform::TransformState &state) { 78ce2e198bSAlex Zinenko FailureOr<LinalgOp> windowed = 79ce2e198bSAlex Zinenko tryApply<DownscaleSizeOneWindowed2DConvolution>(target); 8052307109SNicolas Vasilache if (succeeded(windowed)) { 8152307109SNicolas Vasilache results.push_back(*windowed); 8252307109SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 8352307109SNicolas Vasilache } 84ce2e198bSAlex Zinenko FailureOr<LinalgOp> depthwise = 85ce2e198bSAlex Zinenko tryApply<DownscaleDepthwiseConv2DNhwcHwcOp>(target); 8652307109SNicolas Vasilache if (succeeded(depthwise)) { 8752307109SNicolas Vasilache results.push_back(*depthwise); 8852307109SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 8952307109SNicolas Vasilache } 9052307109SNicolas Vasilache results.assign(1, nullptr); 9152307109SNicolas Vasilache return emitDefaultSilenceableFailure(target); 92ce2e198bSAlex Zinenko } 93ce2e198bSAlex Zinenko 94ce2e198bSAlex Zinenko //===----------------------------------------------------------------------===// 95b6c58ec4SAlex Zinenko // FuseOp 96b6c58ec4SAlex Zinenko //===----------------------------------------------------------------------===// 97b6c58ec4SAlex Zinenko 98b6c58ec4SAlex Zinenko /// Apply a tiling transformation to all payload ops and store both the 99b6c58ec4SAlex Zinenko /// tiled operation as well as the created tile loops. 100b6c58ec4SAlex Zinenko static LogicalResult 1014e4a4c05SAlex Zinenko applyTilingToAll(Operation *transformOp, ArrayRef<Operation *> payloadOps, 1024e4a4c05SAlex Zinenko unsigned numLoops, 103b6c58ec4SAlex Zinenko transform::TransformResults &transformResults, 104b6c58ec4SAlex Zinenko function_ref<FailureOr<TiledLinalgOp>(LinalgOp)> applyFn) { 105b6c58ec4SAlex Zinenko SmallVector<Operation *> tiledLinalgOps; 106b6c58ec4SAlex Zinenko SmallVector<SmallVector<Operation *>> loopOps(numLoops); 107b6c58ec4SAlex Zinenko for (unsigned int i = 0; i < numLoops; ++i) 108b6c58ec4SAlex Zinenko loopOps[i].reserve(payloadOps.size()); 109b6c58ec4SAlex Zinenko 110b6c58ec4SAlex Zinenko for (Operation *target : payloadOps) { 111b6c58ec4SAlex Zinenko auto linalgOp = dyn_cast<linalg::LinalgOp>(target); 112b6c58ec4SAlex Zinenko if (!linalgOp) 113b6c58ec4SAlex Zinenko return transformOp->emitError("only LinalgOps are supported"); 114b6c58ec4SAlex Zinenko 115b6c58ec4SAlex Zinenko FailureOr<TiledLinalgOp> tiled = applyFn(linalgOp); 116b6c58ec4SAlex Zinenko if (failed(tiled)) 117b6c58ec4SAlex Zinenko return failure(); 118b6c58ec4SAlex Zinenko 119b6c58ec4SAlex Zinenko tiledLinalgOps.push_back(tiled->op); 120b6c58ec4SAlex Zinenko if (tiled->loops.size() != numLoops) 121b6c58ec4SAlex Zinenko // Not enough loops were generated. This usually means that the input size 122b6c58ec4SAlex Zinenko // was smaller than the tiling size. 123b6c58ec4SAlex Zinenko // TODO: LinalgTilingPattern should return failure(). 124b6c58ec4SAlex Zinenko return failure(); 125b6c58ec4SAlex Zinenko for (unsigned int i = 0; i < numLoops; ++i) 126b6c58ec4SAlex Zinenko loopOps[i].push_back(tiled->loops[i]); 127b6c58ec4SAlex Zinenko } 128b6c58ec4SAlex Zinenko 129b6c58ec4SAlex Zinenko transformResults.set(transformOp->getOpResult(0), tiledLinalgOps); 130b6c58ec4SAlex Zinenko for (unsigned int i = 0; i < numLoops; ++i) 131b6c58ec4SAlex Zinenko transformResults.set(transformOp->getOpResult(i + 1), loopOps[i]); 132b6c58ec4SAlex Zinenko return success(); 133b6c58ec4SAlex Zinenko } 134b6c58ec4SAlex Zinenko 135b6c58ec4SAlex Zinenko /// Parse a tiling-like operation that returns the tiled op as well as the 136b6c58ec4SAlex Zinenko /// created tile loops. The function counts the non-zero tile sizes to compute 137b6c58ec4SAlex Zinenko /// the number of results. 138b6c58ec4SAlex Zinenko static ParseResult parseTileLikeOp(OpAsmParser &parser, OperationState &result, 139b6c58ec4SAlex Zinenko StringRef sizesAttrName) { 140b6c58ec4SAlex Zinenko OpAsmParser::UnresolvedOperand targetOperand; 141b6c58ec4SAlex Zinenko SMLoc opLoc = parser.getCurrentLocation(); 142b6c58ec4SAlex Zinenko if (parser.parseOperand(targetOperand) || 143b6c58ec4SAlex Zinenko parser.parseOptionalAttrDict(result.attributes)) 144b6c58ec4SAlex Zinenko return failure(); 145b6c58ec4SAlex Zinenko Attribute sizesAttr = result.attributes.get(sizesAttrName); 146b6c58ec4SAlex Zinenko if (!sizesAttr) 147b6c58ec4SAlex Zinenko return parser.emitError(opLoc) 148b6c58ec4SAlex Zinenko << "expected '" << sizesAttrName << "' attribute"; 149b6c58ec4SAlex Zinenko auto sizesArrayAttr = sizesAttr.dyn_cast<ArrayAttr>(); 150b6c58ec4SAlex Zinenko if (!sizesArrayAttr) 151b6c58ec4SAlex Zinenko return parser.emitError(opLoc) 152b6c58ec4SAlex Zinenko << "'" << sizesAttrName << "' attribute must be an array"; 153b6c58ec4SAlex Zinenko Type pdlOpType = parser.getBuilder().getType<pdl::OperationType>(); 154b6c58ec4SAlex Zinenko size_t numExpectedLoops = 15518b92c66SNicolas Vasilache sizesArrayAttr.size() - 15618b92c66SNicolas Vasilache llvm::count(extractFromI64ArrayAttr(sizesArrayAttr), 0); 157b6c58ec4SAlex Zinenko result.addTypes(SmallVector<Type>(numExpectedLoops + 1, pdlOpType)); 158b6c58ec4SAlex Zinenko if (parser.resolveOperand(targetOperand, pdlOpType, result.operands)) 159b6c58ec4SAlex Zinenko return failure(); 160b6c58ec4SAlex Zinenko return success(); 161b6c58ec4SAlex Zinenko } 162b6c58ec4SAlex Zinenko 1631d45282aSAlex Zinenko DiagnosedSilenceableFailure 164b6c58ec4SAlex Zinenko transform::FuseOp::apply(mlir::transform::TransformResults &transformResults, 165b6c58ec4SAlex Zinenko mlir::transform::TransformState &state) { 166b6c58ec4SAlex Zinenko LinalgTilingAndFusionOptions fusionOptions; 16718b92c66SNicolas Vasilache fusionOptions.tileSizes = extractFromI64ArrayAttr(getTileSizes()); 16818b92c66SNicolas Vasilache fusionOptions.tileInterchange = extractFromI64ArrayAttr(getTileInterchange()); 169b6c58ec4SAlex Zinenko 170e3890b7fSAlex Zinenko LogicalResult result = applyTilingToAll( 1714e4a4c05SAlex Zinenko getOperation(), state.getPayloadOps(getTarget()), 1724e4a4c05SAlex Zinenko fusionOptions.tileSizes.size() - llvm::count(fusionOptions.tileSizes, 0), 1734e4a4c05SAlex Zinenko transformResults, [&](LinalgOp linalgOp) -> FailureOr<TiledLinalgOp> { 174b6c58ec4SAlex Zinenko LinalgTileAndFuseTensorOpsPattern pattern(getContext(), fusionOptions); 175b6c58ec4SAlex Zinenko SimpleRewriter rewriter(getContext()); 176b6c58ec4SAlex Zinenko rewriter.setInsertionPoint(linalgOp); 177b6c58ec4SAlex Zinenko FailureOr<TileLoopNest> tileLoopNest = 178b6c58ec4SAlex Zinenko pattern.returningMatchAndRewrite(linalgOp, rewriter); 179b6c58ec4SAlex Zinenko if (failed(tileLoopNest)) 180b6c58ec4SAlex Zinenko return failure(); 181b6c58ec4SAlex Zinenko 182b6c58ec4SAlex Zinenko TiledLinalgOp tiledLinalgOp; 183b6c58ec4SAlex Zinenko tiledLinalgOp.op = tileLoopNest->getRootOp(); 184b6c58ec4SAlex Zinenko tiledLinalgOp.loops = {tileLoopNest->getLoopOps().begin(), 185b6c58ec4SAlex Zinenko tileLoopNest->getLoopOps().end()}; 186b6c58ec4SAlex Zinenko return tiledLinalgOp; 187b6c58ec4SAlex Zinenko }); 1884e4a4c05SAlex Zinenko return DiagnosedSilenceableFailure(result); 189b6c58ec4SAlex Zinenko } 190b6c58ec4SAlex Zinenko 191b6c58ec4SAlex Zinenko ParseResult transform::FuseOp::parse(OpAsmParser &parser, 192b6c58ec4SAlex Zinenko OperationState &result) { 193b6c58ec4SAlex Zinenko return parseTileLikeOp( 194b6c58ec4SAlex Zinenko parser, result, 195b6c58ec4SAlex Zinenko transform::FuseOp::getTileSizesAttrName(result.name).getValue()); 196b6c58ec4SAlex Zinenko } 197b6c58ec4SAlex Zinenko 198b6c58ec4SAlex Zinenko void transform::FuseOp::print(OpAsmPrinter &p) { 199b6c58ec4SAlex Zinenko p << ' '; 200b6c58ec4SAlex Zinenko p << getTarget(); 201b6c58ec4SAlex Zinenko p.printOptionalAttrDict((*this)->getAttrs()); 202b6c58ec4SAlex Zinenko } 203b6c58ec4SAlex Zinenko 204b6c58ec4SAlex Zinenko LogicalResult transform::FuseOp::verify() { 20518b92c66SNicolas Vasilache SmallVector<int64_t> permutation = 20618b92c66SNicolas Vasilache extractFromI64ArrayAttr(getTileInterchange()); 207b6c58ec4SAlex Zinenko auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, permutation.size())); 208b6c58ec4SAlex Zinenko if (!std::is_permutation(sequence.begin(), sequence.end(), 209b6c58ec4SAlex Zinenko permutation.begin(), permutation.end())) { 210b6c58ec4SAlex Zinenko return emitOpError() << "expects interchange to be a permutation, found " 211b6c58ec4SAlex Zinenko << getTileInterchange(); 212b6c58ec4SAlex Zinenko } 213b6c58ec4SAlex Zinenko return success(); 214b6c58ec4SAlex Zinenko } 215b6c58ec4SAlex Zinenko 216b6c58ec4SAlex Zinenko //===----------------------------------------------------------------------===// 217*bc882ed2SMatthias Springer // FuseIntoContainingOp 218*bc882ed2SMatthias Springer //===----------------------------------------------------------------------===// 219*bc882ed2SMatthias Springer 220*bc882ed2SMatthias Springer static FailureOr<SmallVector<Operation *>> tileAndFuse(Operation *producerOp, 221*bc882ed2SMatthias Springer Operation *containingOp, 222*bc882ed2SMatthias Springer RewriterBase &rewriter) { 223*bc882ed2SMatthias Springer auto tileableProducer = dyn_cast<TilingInterface>(producerOp); 224*bc882ed2SMatthias Springer if (!tileableProducer) 225*bc882ed2SMatthias Springer return failure(); 226*bc882ed2SMatthias Springer 227*bc882ed2SMatthias Springer // Search the producer slices accessed within the containing operation. 228*bc882ed2SMatthias Springer // TODO: Generalize to more extract/insert/parallel_insert triples. Maybe 229*bc882ed2SMatthias Springer // evolve into an interface. 230*bc882ed2SMatthias Springer SmallVector<tensor::ExtractSliceOp> sliceOps; 231*bc882ed2SMatthias Springer for (Operation *user : tileableProducer->getUsers()) { 232*bc882ed2SMatthias Springer auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(user); 233*bc882ed2SMatthias Springer if (!sliceOp) 234*bc882ed2SMatthias Springer continue; 235*bc882ed2SMatthias Springer if (!containingOp->isProperAncestor(sliceOp)) 236*bc882ed2SMatthias Springer continue; 237*bc882ed2SMatthias Springer sliceOps.push_back(sliceOp); 238*bc882ed2SMatthias Springer } 239*bc882ed2SMatthias Springer 240*bc882ed2SMatthias Springer // Check for a non-empty list of fusion opportunities. 241*bc882ed2SMatthias Springer if (sliceOps.empty()) 242*bc882ed2SMatthias Springer return failure(); 243*bc882ed2SMatthias Springer 244*bc882ed2SMatthias Springer SmallVector<Value> destinationOperands = 245*bc882ed2SMatthias Springer tileableProducer.getDestinationOperands(rewriter); 246*bc882ed2SMatthias Springer 247*bc882ed2SMatthias Springer // Try to fuse the producer in-place. 248*bc882ed2SMatthias Springer SmallVector<Operation *> fusedOps; 249*bc882ed2SMatthias Springer for (tensor::ExtractSliceOp sliceOp : sliceOps) { 250*bc882ed2SMatthias Springer OpBuilder::InsertionGuard guard(rewriter); 251*bc882ed2SMatthias Springer rewriter.setInsertionPoint(sliceOp); 252*bc882ed2SMatthias Springer 253*bc882ed2SMatthias Springer // Tile the producer. 254*bc882ed2SMatthias Springer FailureOr<Value> tiledProducer = tileableProducer.generateResultTileValue( 255*bc882ed2SMatthias Springer rewriter, /*resultNumber=*/0, destinationOperands, 256*bc882ed2SMatthias Springer sliceOp.getMixedOffsets(), sliceOp.getMixedSizes(), true); 257*bc882ed2SMatthias Springer if (failed(tiledProducer)) 258*bc882ed2SMatthias Springer return failure(); 259*bc882ed2SMatthias Springer fusedOps.push_back(tiledProducer->getDefiningOp()); 260*bc882ed2SMatthias Springer } 261*bc882ed2SMatthias Springer 262*bc882ed2SMatthias Springer // Replace the extract op. 263*bc882ed2SMatthias Springer for (const auto &en : enumerate(sliceOps)) 264*bc882ed2SMatthias Springer rewriter.replaceOp(en.value(), fusedOps[en.index()]->getResult(0)); 265*bc882ed2SMatthias Springer return fusedOps; 266*bc882ed2SMatthias Springer } 267*bc882ed2SMatthias Springer 268*bc882ed2SMatthias Springer static FailureOr<SmallVector<Operation *>> 269*bc882ed2SMatthias Springer cloneAndFuse(Operation *producerOp, Operation *containingOp, 270*bc882ed2SMatthias Springer RewriterBase &rewriter) { 271*bc882ed2SMatthias Springer // Gather all uses inside the containing op. 272*bc882ed2SMatthias Springer SmallVector<OpOperand *> uses; 273*bc882ed2SMatthias Springer for (OpResult result : producerOp->getOpResults()) 274*bc882ed2SMatthias Springer for (OpOperand &use : result.getUses()) 275*bc882ed2SMatthias Springer if (containingOp->isProperAncestor(use.getOwner())) 276*bc882ed2SMatthias Springer uses.push_back(&use); 277*bc882ed2SMatthias Springer 278*bc882ed2SMatthias Springer // Check for a non-empty list of fusion opportunities. 279*bc882ed2SMatthias Springer if (uses.empty()) 280*bc882ed2SMatthias Springer return failure(); 281*bc882ed2SMatthias Springer 282*bc882ed2SMatthias Springer // Clone and fuse inside the containing op. 283*bc882ed2SMatthias Springer SmallVector<Operation *> fusedOps; 284*bc882ed2SMatthias Springer for (OpOperand *use : uses) { 285*bc882ed2SMatthias Springer unsigned resultNumber = use->get().cast<OpResult>().getResultNumber(); 286*bc882ed2SMatthias Springer OpBuilder::InsertionGuard guard(rewriter); 287*bc882ed2SMatthias Springer rewriter.setInsertionPoint(use->getOwner()); 288*bc882ed2SMatthias Springer Operation *cloned = rewriter.clone(*producerOp); 289*bc882ed2SMatthias Springer rewriter.updateRootInPlace( 290*bc882ed2SMatthias Springer use->getOwner(), [&] { use->set(cloned->getOpResult(resultNumber)); }); 291*bc882ed2SMatthias Springer fusedOps.push_back(cloned); 292*bc882ed2SMatthias Springer } 293*bc882ed2SMatthias Springer 294*bc882ed2SMatthias Springer return fusedOps; 295*bc882ed2SMatthias Springer } 296*bc882ed2SMatthias Springer 297*bc882ed2SMatthias Springer DiagnosedSilenceableFailure 298*bc882ed2SMatthias Springer transform::FuseIntoContainingOp::apply(transform::TransformResults &results, 299*bc882ed2SMatthias Springer transform::TransformState &state) { 300*bc882ed2SMatthias Springer SmallVector<Operation *> fusedOps; 301*bc882ed2SMatthias Springer ArrayRef<Operation *> producerOps = state.getPayloadOps(getProducerOp()); 302*bc882ed2SMatthias Springer for (Operation *producerOp : producerOps) { 303*bc882ed2SMatthias Springer if (producerOp->getNumResults() != 1) { 304*bc882ed2SMatthias Springer Diagnostic diag(producerOp->getLoc(), DiagnosticSeverity::Note); 305*bc882ed2SMatthias Springer diag << "op with != 1 results not supported"; 306*bc882ed2SMatthias Springer return DiagnosedSilenceableFailure::silenceableFailure(std::move(diag)); 307*bc882ed2SMatthias Springer } 308*bc882ed2SMatthias Springer } 309*bc882ed2SMatthias Springer ArrayRef<Operation *> containingOps = state.getPayloadOps(getContainingOp()); 310*bc882ed2SMatthias Springer if (containingOps.size() != 1) 311*bc882ed2SMatthias Springer return DiagnosedSilenceableFailure( 312*bc882ed2SMatthias Springer this->emitOpError("requires exactly one containing_op handle")); 313*bc882ed2SMatthias Springer Operation *containingOp = containingOps.front(); 314*bc882ed2SMatthias Springer 315*bc882ed2SMatthias Springer // Helper function to find the next producer that should be fused. Take any 316*bc882ed2SMatthias Springer // producer that has a use inside the containing op. 317*bc882ed2SMatthias Springer SmallVector<Operation *> remainingProducers(producerOps.begin(), 318*bc882ed2SMatthias Springer producerOps.end()); 319*bc882ed2SMatthias Springer auto getNextProducer = [&]() -> FailureOr<Operation *> { 320*bc882ed2SMatthias Springer for (const auto &it : enumerate(remainingProducers)) { 321*bc882ed2SMatthias Springer Operation *producerOp = it.value(); 322*bc882ed2SMatthias Springer bool hasUseInContainingOp = 323*bc882ed2SMatthias Springer any_of(producerOp->getUsers(), [&](Operation *op) { 324*bc882ed2SMatthias Springer return containingOp->isProperAncestor(op); 325*bc882ed2SMatthias Springer }); 326*bc882ed2SMatthias Springer // TODO: When resolving the TODO below (no duplicate ops), take an op that 327*bc882ed2SMatthias Springer // has no use among the remaining producers. This is a topological 328*bc882ed2SMatthias Springer // sorting. 329*bc882ed2SMatthias Springer if (hasUseInContainingOp) { 330*bc882ed2SMatthias Springer remainingProducers.erase(remainingProducers.begin() + it.index()); 331*bc882ed2SMatthias Springer return producerOp; 332*bc882ed2SMatthias Springer } 333*bc882ed2SMatthias Springer } 334*bc882ed2SMatthias Springer return failure(); 335*bc882ed2SMatthias Springer }; 336*bc882ed2SMatthias Springer 337*bc882ed2SMatthias Springer IRRewriter rewriter(getContext()); 338*bc882ed2SMatthias Springer while (!remainingProducers.empty()) { 339*bc882ed2SMatthias Springer auto nextProducer = getNextProducer(); 340*bc882ed2SMatthias Springer if (failed(nextProducer)) { 341*bc882ed2SMatthias Springer Diagnostic diag(containingOp->getLoc(), DiagnosticSeverity::Note); 342*bc882ed2SMatthias Springer diag << "could not fuse ops into container"; 343*bc882ed2SMatthias Springer return DiagnosedSilenceableFailure::silenceableFailure(std::move(diag)); 344*bc882ed2SMatthias Springer } 345*bc882ed2SMatthias Springer 346*bc882ed2SMatthias Springer Operation *producerOp = *nextProducer; 347*bc882ed2SMatthias Springer // TODO: If there are multiple uses of the producer in the containing op, we 348*bc882ed2SMatthias Springer // currently tile/clone the op multiple times (once per use). In some cases, 349*bc882ed2SMatthias Springer // we can tile/clone once and reuse the value for each use. Futhermore, 350*bc882ed2SMatthias Springer // producers should then be traversed according to a topological sorting. 351*bc882ed2SMatthias Springer auto tiled = tileAndFuse(producerOp, containingOp, rewriter); 352*bc882ed2SMatthias Springer if (succeeded(tiled)) 353*bc882ed2SMatthias Springer fusedOps.append(*tiled); 354*bc882ed2SMatthias Springer 355*bc882ed2SMatthias Springer auto cloned = cloneAndFuse(producerOp, containingOp, rewriter); 356*bc882ed2SMatthias Springer if (succeeded(cloned)) 357*bc882ed2SMatthias Springer fusedOps.append(*cloned); 358*bc882ed2SMatthias Springer 359*bc882ed2SMatthias Springer if (failed(tiled) && failed(cloned)) { 360*bc882ed2SMatthias Springer Diagnostic diag(producerOp->getLoc(), DiagnosticSeverity::Note); 361*bc882ed2SMatthias Springer diag << "could not fuse into containing op"; 362*bc882ed2SMatthias Springer return DiagnosedSilenceableFailure::silenceableFailure(std::move(diag)); 363*bc882ed2SMatthias Springer } 364*bc882ed2SMatthias Springer } 365*bc882ed2SMatthias Springer 366*bc882ed2SMatthias Springer results.set(getFusedOp().cast<OpResult>(), fusedOps); 367*bc882ed2SMatthias Springer return DiagnosedSilenceableFailure::success(); 368*bc882ed2SMatthias Springer } 369*bc882ed2SMatthias Springer 370*bc882ed2SMatthias Springer //===----------------------------------------------------------------------===// 371ce2e198bSAlex Zinenko // GeneralizeOp 372ce2e198bSAlex Zinenko //===----------------------------------------------------------------------===// 373ce2e198bSAlex Zinenko 37452307109SNicolas Vasilache DiagnosedSilenceableFailure 37552307109SNicolas Vasilache transform::GeneralizeOp::applyToOne(linalg::LinalgOp target, 37652307109SNicolas Vasilache SmallVectorImpl<Operation *> &results, 37752307109SNicolas Vasilache transform::TransformState &state) { 378ce2e198bSAlex Zinenko // Exit early if no transformation is needed. 37952307109SNicolas Vasilache if (isa<GenericOp>(target)) { 38052307109SNicolas Vasilache results.push_back(target); 38152307109SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 38252307109SNicolas Vasilache } 383ce2e198bSAlex Zinenko FailureOr<LinalgOp> generic = tryApply<LinalgGeneralizationPattern>(target); 38452307109SNicolas Vasilache if (succeeded(generic)) { 38552307109SNicolas Vasilache results.push_back(generic->getOperation()); 38652307109SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 38752307109SNicolas Vasilache } 38852307109SNicolas Vasilache results.assign(1, nullptr); 38952307109SNicolas Vasilache return emitDefaultSilenceableFailure(target); 390ce2e198bSAlex Zinenko } 391ce2e198bSAlex Zinenko 3923c2a74a3SMatthias Springer //===----------------------------------------------------------------------===// 3935cde5a57SAlex Zinenko // InterchangeOp 3945cde5a57SAlex Zinenko //===----------------------------------------------------------------------===// 3955cde5a57SAlex Zinenko 39652307109SNicolas Vasilache DiagnosedSilenceableFailure 39752307109SNicolas Vasilache transform::InterchangeOp::applyToOne(linalg::GenericOp target, 39852307109SNicolas Vasilache SmallVectorImpl<Operation *> &results, 39952307109SNicolas Vasilache transform::TransformState &state) { 4005cde5a57SAlex Zinenko SmallVector<unsigned> interchangeVector = 4015cde5a57SAlex Zinenko extractUIntArray(getIteratorInterchange()); 4025cde5a57SAlex Zinenko // Exit early if no transformation is needed. 40352307109SNicolas Vasilache if (interchangeVector.empty()) { 40452307109SNicolas Vasilache results.push_back(target); 40552307109SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 4065cde5a57SAlex Zinenko } 40752307109SNicolas Vasilache SimpleRewriter rewriter(target->getContext()); 40852307109SNicolas Vasilache FailureOr<GenericOp> res = 40952307109SNicolas Vasilache interchangeGenericOp(rewriter, target, interchangeVector); 41052307109SNicolas Vasilache if (failed(res)) 41152307109SNicolas Vasilache return DiagnosedSilenceableFailure::definiteFailure(); 41252307109SNicolas Vasilache results.push_back(res->getOperation()); 41352307109SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 4145cde5a57SAlex Zinenko } 4155cde5a57SAlex Zinenko 4165cde5a57SAlex Zinenko LogicalResult transform::InterchangeOp::verify() { 4175cde5a57SAlex Zinenko SmallVector<unsigned> permutation = 4185cde5a57SAlex Zinenko extractUIntArray(getIteratorInterchange()); 4195cde5a57SAlex Zinenko auto sequence = llvm::to_vector(llvm::seq<unsigned>(0, permutation.size())); 4205cde5a57SAlex Zinenko if (!std::is_permutation(sequence.begin(), sequence.end(), 4215cde5a57SAlex Zinenko permutation.begin(), permutation.end())) { 4225cde5a57SAlex Zinenko return emitOpError() 4235cde5a57SAlex Zinenko << "expects iterator_interchange to be a permutation, found " 4245cde5a57SAlex Zinenko << getIteratorInterchange(); 4255cde5a57SAlex Zinenko } 4265cde5a57SAlex Zinenko return success(); 4275cde5a57SAlex Zinenko } 4285cde5a57SAlex Zinenko 4295cde5a57SAlex Zinenko //===---------------------------------------------------------------------===// 4301f77f01cSNicolas Vasilache // MatchOp 4311f77f01cSNicolas Vasilache //===---------------------------------------------------------------------===// 4321f77f01cSNicolas Vasilache 4331f77f01cSNicolas Vasilache LogicalResult transform::MatchOp::verify() { 4341f77f01cSNicolas Vasilache bool opXorIface = getOps().hasValue() ^ getInterface().hasValue(); 4351f77f01cSNicolas Vasilache if (!opXorIface) 4361f77f01cSNicolas Vasilache return this->emitOpError( 4371f77f01cSNicolas Vasilache "requires a either a match_op or a match_interface attribute (but not " 4381f77f01cSNicolas Vasilache "both)"); 4391f77f01cSNicolas Vasilache return success(); 4401f77f01cSNicolas Vasilache } 4411f77f01cSNicolas Vasilache 4421f77f01cSNicolas Vasilache DiagnosedSilenceableFailure 4431f77f01cSNicolas Vasilache transform::MatchOp::apply(transform::TransformResults &results, 4441f77f01cSNicolas Vasilache transform::TransformState &state) { 4451f77f01cSNicolas Vasilache llvm::StringSet<> strs; 4461f77f01cSNicolas Vasilache if (getOps().hasValue()) 4471f77f01cSNicolas Vasilache strs.insert(getOps()->getAsValueRange<StringAttr>().begin(), 4481f77f01cSNicolas Vasilache getOps()->getAsValueRange<StringAttr>().end()); 4491f77f01cSNicolas Vasilache 4501f77f01cSNicolas Vasilache ArrayRef<Operation *> payloadOps = state.getPayloadOps(getTarget()); 4511f77f01cSNicolas Vasilache if (payloadOps.size() != 1) 4521f77f01cSNicolas Vasilache return DiagnosedSilenceableFailure( 4531f77f01cSNicolas Vasilache this->emitOpError("requires exactly one target handle")); 4541f77f01cSNicolas Vasilache 4551f77f01cSNicolas Vasilache SmallVector<Operation *> res; 4561f77f01cSNicolas Vasilache 4571f77f01cSNicolas Vasilache auto matchFun = [&](Operation *op) { 4581f77f01cSNicolas Vasilache if (strs.contains(op->getName().getStringRef())) 4591f77f01cSNicolas Vasilache res.push_back(op); 4601f77f01cSNicolas Vasilache // Interfaces cannot be matched by name, just by ID. 4611f77f01cSNicolas Vasilache // So we specifically encode the interfaces we care about for this op. 4621f77f01cSNicolas Vasilache if (getInterface().hasValue()) { 4631f77f01cSNicolas Vasilache auto iface = getInterface().getValue(); 4641f77f01cSNicolas Vasilache if (iface == transform::MatchInterfaceEnum::LinalgOp && 4651f77f01cSNicolas Vasilache isa<linalg::LinalgOp>(op)) 4661f77f01cSNicolas Vasilache res.push_back(op); 4671f77f01cSNicolas Vasilache if (iface == transform::MatchInterfaceEnum::TilingInterface && 4681f77f01cSNicolas Vasilache isa<TilingInterface>(op)) 4691f77f01cSNicolas Vasilache res.push_back(op); 4701f77f01cSNicolas Vasilache } 4711f77f01cSNicolas Vasilache }; 4721f77f01cSNicolas Vasilache 4731f77f01cSNicolas Vasilache payloadOps.front()->walk(matchFun); 4741f77f01cSNicolas Vasilache results.set(getResult().cast<OpResult>(), res); 4751f77f01cSNicolas Vasilache return DiagnosedSilenceableFailure(success()); 4761f77f01cSNicolas Vasilache } 4771f77f01cSNicolas Vasilache 4781f77f01cSNicolas Vasilache ParseResult transform::MatchOp::parse(OpAsmParser &parser, 4791f77f01cSNicolas Vasilache OperationState &result) { 4801f77f01cSNicolas Vasilache // Parse 'match_op' or 'interface' clause. 4811f77f01cSNicolas Vasilache if (succeeded(parser.parseOptionalKeyword("ops"))) { 4821f77f01cSNicolas Vasilache ArrayAttr opsAttr; 4831f77f01cSNicolas Vasilache if (parser.parseLBrace() || 4841f77f01cSNicolas Vasilache parser.parseCustomAttributeWithFallback( 4851f77f01cSNicolas Vasilache opsAttr, parser.getBuilder().getType<NoneType>(), "ops", 4861f77f01cSNicolas Vasilache result.attributes) || 4871f77f01cSNicolas Vasilache parser.parseRBrace()) 4881f77f01cSNicolas Vasilache return failure(); 4891f77f01cSNicolas Vasilache } else if (succeeded(parser.parseOptionalKeyword("interface"))) { 4901f77f01cSNicolas Vasilache if (parser.parseLBrace()) 4911f77f01cSNicolas Vasilache return failure(); 4921f77f01cSNicolas Vasilache StringRef attrStr; 4931f77f01cSNicolas Vasilache auto loc = parser.getCurrentLocation(); 4941f77f01cSNicolas Vasilache if (parser.parseKeyword(&attrStr)) 4951f77f01cSNicolas Vasilache return failure(); 4961f77f01cSNicolas Vasilache auto interfaceEnum = transform::symbolizeMatchInterfaceEnum(attrStr); 4971f77f01cSNicolas Vasilache if (!interfaceEnum) 4981f77f01cSNicolas Vasilache return parser.emitError(loc, "invalid ") 4991f77f01cSNicolas Vasilache << "match_interface attribute specification: \"" << attrStr << '"'; 5001f77f01cSNicolas Vasilache transform::MatchInterfaceEnumAttr match_interfaceAttr = 5011f77f01cSNicolas Vasilache transform::MatchInterfaceEnumAttr::get(parser.getBuilder().getContext(), 5021f77f01cSNicolas Vasilache interfaceEnum.value()); 5031f77f01cSNicolas Vasilache result.addAttribute("interface", match_interfaceAttr); 5041f77f01cSNicolas Vasilache if (parser.parseRBrace()) 5051f77f01cSNicolas Vasilache return failure(); 5061f77f01cSNicolas Vasilache } else { 5071f77f01cSNicolas Vasilache auto loc = parser.getCurrentLocation(); 5081f77f01cSNicolas Vasilache return parser.emitError(loc, "expected ops or interface"); 5091f77f01cSNicolas Vasilache } 5101f77f01cSNicolas Vasilache 5111f77f01cSNicolas Vasilache OpAsmParser::UnresolvedOperand targetRawOperands[1]; 5121f77f01cSNicolas Vasilache ArrayRef<OpAsmParser::UnresolvedOperand> targetOperands(targetRawOperands); 5131f77f01cSNicolas Vasilache if (parser.parseKeyword("in") || parser.parseOperand(targetRawOperands[0]) || 5141f77f01cSNicolas Vasilache parser.parseOptionalAttrDict(result.attributes)) 5151f77f01cSNicolas Vasilache return failure(); 5161f77f01cSNicolas Vasilache Type pdlOpType = parser.getBuilder().getType<pdl::OperationType>(); 5171f77f01cSNicolas Vasilache result.addTypes(pdlOpType); 5181f77f01cSNicolas Vasilache if (parser.resolveOperands(targetOperands, pdlOpType, result.operands)) 5191f77f01cSNicolas Vasilache return failure(); 5201f77f01cSNicolas Vasilache return success(); 5211f77f01cSNicolas Vasilache } 5221f77f01cSNicolas Vasilache 5231f77f01cSNicolas Vasilache void transform::MatchOp::print(OpAsmPrinter &p) { 5241f77f01cSNicolas Vasilache if ((*this)->getAttr("ops")) { 5251f77f01cSNicolas Vasilache p << " ops{"; 5261f77f01cSNicolas Vasilache p.printAttributeWithoutType(getOpsAttr()); 5271f77f01cSNicolas Vasilache p << "}"; 5281f77f01cSNicolas Vasilache } 5291f77f01cSNicolas Vasilache if ((*this)->getAttr("interface")) { 5301f77f01cSNicolas Vasilache p << " interface{" << stringifyMatchInterfaceEnum(*getInterface()) << "}"; 5311f77f01cSNicolas Vasilache } 5321f77f01cSNicolas Vasilache p << " in " << getTarget(); 5331f77f01cSNicolas Vasilache p.printOptionalAttrDict((*this)->getAttrs(), 5341f77f01cSNicolas Vasilache /*elidedAttrs=*/{"ops", "interface"}); 5351f77f01cSNicolas Vasilache } 5361f77f01cSNicolas Vasilache 5371f77f01cSNicolas Vasilache //===---------------------------------------------------------------------===// 5383963b4d0SAlex Zinenko // MultiTileSizesOp 5393963b4d0SAlex Zinenko //===---------------------------------------------------------------------===// 5403963b4d0SAlex Zinenko 5413963b4d0SAlex Zinenko DiagnosedSilenceableFailure transform::MultiTileSizesOp::applyToOne( 5423963b4d0SAlex Zinenko LinalgOp target, SmallVector<Operation *> &results, TransformState &state) { 5433963b4d0SAlex Zinenko OpBuilder builder(target.getContext()); 5443963b4d0SAlex Zinenko builder.setInsertionPoint(target); 5453963b4d0SAlex Zinenko OpFoldResult targetSize = builder.getIndexAttr(getTargetSize()); 5463963b4d0SAlex Zinenko OpFoldResult divisor = builder.getIndexAttr(getDivisor()); 5473963b4d0SAlex Zinenko FailureOr<MultiSizeSpecification> spec = computeMultiTileSizes( 5483963b4d0SAlex Zinenko builder, target, getDimension(), targetSize, divisor); 5493963b4d0SAlex Zinenko if (failed(spec)) { 5503963b4d0SAlex Zinenko return emitSilenceableError() << "could not generate tile size computation"; 5513963b4d0SAlex Zinenko } 5523963b4d0SAlex Zinenko 553a5c802a4SAlex Zinenko AffineExpr s0 = builder.getAffineSymbolExpr(0); 554a5c802a4SAlex Zinenko AffineExpr s1 = builder.getAffineSymbolExpr(1); 5553963b4d0SAlex Zinenko Operation *splitPoint = 556a5c802a4SAlex Zinenko makeComposedAffineApply(builder, target.getLoc(), s0 * s1, 557a5c802a4SAlex Zinenko {spec->lowTileSize, spec->lowTripCount}); 5583963b4d0SAlex Zinenko Operation *lowTileSize = spec->lowTileSize.getDefiningOp(); 5593963b4d0SAlex Zinenko Operation *highTileSize = spec->highTileSize.getDefiningOp(); 5603963b4d0SAlex Zinenko assert(lowTileSize && highTileSize && splitPoint && 5613963b4d0SAlex Zinenko "tile sizes are not produced by operations"); 5623963b4d0SAlex Zinenko results.reserve(results.size() + 3); 5633963b4d0SAlex Zinenko results.push_back(lowTileSize); 5643963b4d0SAlex Zinenko results.push_back(highTileSize); 5653963b4d0SAlex Zinenko results.push_back(splitPoint); 5663963b4d0SAlex Zinenko return DiagnosedSilenceableFailure::success(); 5673963b4d0SAlex Zinenko } 5683963b4d0SAlex Zinenko 5693963b4d0SAlex Zinenko void transform::MultiTileSizesOp::getEffects( 5703963b4d0SAlex Zinenko SmallVectorImpl<MemoryEffects::EffectInstance> &effects) { 571e15b855eSAlex Zinenko onlyReadsHandle(getTarget(), effects); 572e15b855eSAlex Zinenko producesHandle(getResults(), effects); 573e15b855eSAlex Zinenko modifiesPayload(effects); 5743963b4d0SAlex Zinenko } 5753963b4d0SAlex Zinenko 5763963b4d0SAlex Zinenko //===---------------------------------------------------------------------===// 5775cde5a57SAlex Zinenko // PadOp 5785cde5a57SAlex Zinenko //===---------------------------------------------------------------------===// 5795cde5a57SAlex Zinenko 58052307109SNicolas Vasilache DiagnosedSilenceableFailure 58152307109SNicolas Vasilache transform::PadOp::applyToOne(linalg::LinalgOp target, 58252307109SNicolas Vasilache SmallVectorImpl<Operation *> &results, 58352307109SNicolas Vasilache transform::TransformState &state) { 5845cde5a57SAlex Zinenko // Convert the integer packing flags to booleans. 5855cde5a57SAlex Zinenko SmallVector<bool> packPaddings; 58618b92c66SNicolas Vasilache for (int64_t packPadding : extractFromI64ArrayAttr(getPackPaddings())) 5875cde5a57SAlex Zinenko packPaddings.push_back(static_cast<bool>(packPadding)); 5885cde5a57SAlex Zinenko 5895cde5a57SAlex Zinenko // Convert the padding values to attributes. 5905cde5a57SAlex Zinenko SmallVector<Attribute> paddingValues; 5915cde5a57SAlex Zinenko for (auto const &it : 5925cde5a57SAlex Zinenko llvm::zip(getPaddingValues(), target->getOperandTypes())) { 5935cde5a57SAlex Zinenko Attribute attr = std::get<0>(it); 5945cde5a57SAlex Zinenko Type elementType = getElementTypeOrSelf(std::get<1>(it)); 5955cde5a57SAlex Zinenko // Try to parse string attributes to obtain an attribute of element type. 5965cde5a57SAlex Zinenko if (auto stringAttr = attr.dyn_cast<StringAttr>()) { 5975cde5a57SAlex Zinenko paddingValues.push_back( 5985cde5a57SAlex Zinenko parseAttribute(attr.cast<StringAttr>(), elementType)); 5995cde5a57SAlex Zinenko if (!paddingValues.back()) { 60052307109SNicolas Vasilache auto diag = this->emitOpError("expects a padding that parses to ") 6015cde5a57SAlex Zinenko << elementType << ", got " << std::get<0>(it); 6025cde5a57SAlex Zinenko diag.attachNote(target.getLoc()) << "when applied to this op"; 60352307109SNicolas Vasilache return DiagnosedSilenceableFailure::definiteFailure(); 6045cde5a57SAlex Zinenko } 6055cde5a57SAlex Zinenko continue; 6065cde5a57SAlex Zinenko } 6075cde5a57SAlex Zinenko // Otherwise, add the attribute directly. 6085cde5a57SAlex Zinenko if (attr.getType() != elementType) { 60952307109SNicolas Vasilache auto diag = this->emitOpError("expects a padding value of type ") 6105cde5a57SAlex Zinenko << elementType << ", got " << attr; 6115cde5a57SAlex Zinenko diag.attachNote(target.getLoc()) << "when applied to this op"; 61252307109SNicolas Vasilache return DiagnosedSilenceableFailure::definiteFailure(); 6135cde5a57SAlex Zinenko } 6145cde5a57SAlex Zinenko paddingValues.push_back(attr); 6155cde5a57SAlex Zinenko } 6165cde5a57SAlex Zinenko 6175cde5a57SAlex Zinenko // Extract the transpose vectors. 6185cde5a57SAlex Zinenko SmallVector<SmallVector<int64_t>> transposePaddings; 6195cde5a57SAlex Zinenko for (Attribute transposeVector : getTransposePaddings().cast<ArrayAttr>()) 6205cde5a57SAlex Zinenko transposePaddings.push_back( 62118b92c66SNicolas Vasilache extractFromI64ArrayAttr(transposeVector.cast<ArrayAttr>())); 6225cde5a57SAlex Zinenko 6235cde5a57SAlex Zinenko LinalgPaddingOptions paddingOptions; 6245cde5a57SAlex Zinenko paddingOptions.setPaddingValues(paddingValues); 62518b92c66SNicolas Vasilache paddingOptions.setPaddingDimensions( 62618b92c66SNicolas Vasilache extractFromI64ArrayAttr(getPaddingDimensions())); 6275cde5a57SAlex Zinenko paddingOptions.setPackPaddings(packPaddings); 62818b92c66SNicolas Vasilache paddingOptions.setHoistPaddings(extractFromI64ArrayAttr(getHoistPaddings())); 6295cde5a57SAlex Zinenko paddingOptions.setTransposePaddings(transposePaddings); 6305cde5a57SAlex Zinenko 631ce2e198bSAlex Zinenko FailureOr<LinalgOp> result = 632ce2e198bSAlex Zinenko tryApply<LinalgPaddingPattern>(target, paddingOptions); 63352307109SNicolas Vasilache if (succeeded(result)) { 63452307109SNicolas Vasilache results.push_back(result->getOperation()); 63552307109SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 63652307109SNicolas Vasilache } 637ce2e198bSAlex Zinenko 63852307109SNicolas Vasilache results.assign(1, nullptr); 63952307109SNicolas Vasilache return emitDefaultSilenceableFailure(target); 6405cde5a57SAlex Zinenko } 6415cde5a57SAlex Zinenko 6425cde5a57SAlex Zinenko LogicalResult transform::PadOp::verify() { 64318b92c66SNicolas Vasilache SmallVector<int64_t> packPaddings = 64418b92c66SNicolas Vasilache extractFromI64ArrayAttr(getPackPaddings()); 6455cde5a57SAlex Zinenko if (any_of(packPaddings, [](int64_t packPadding) { 6465cde5a57SAlex Zinenko return packPadding != 0 && packPadding != 1; 6475cde5a57SAlex Zinenko })) { 6485cde5a57SAlex Zinenko return emitOpError() 6495cde5a57SAlex Zinenko << "expects pack_paddings to contain booleans (0/1), found " 6505cde5a57SAlex Zinenko << getPackPaddings(); 6515cde5a57SAlex Zinenko } 6525cde5a57SAlex Zinenko 6535cde5a57SAlex Zinenko SmallVector<int64_t> paddingDimensions = 65418b92c66SNicolas Vasilache extractFromI64ArrayAttr(getPaddingDimensions()); 6555cde5a57SAlex Zinenko if (any_of(paddingDimensions, 6565cde5a57SAlex Zinenko [](int64_t paddingDimension) { return paddingDimension < 0; })) { 6575cde5a57SAlex Zinenko return emitOpError() 6585cde5a57SAlex Zinenko << "expects padding_dimensions to contain positive integers, found " 6595cde5a57SAlex Zinenko << getPaddingDimensions(); 6605cde5a57SAlex Zinenko } 6615cde5a57SAlex Zinenko 66218b92c66SNicolas Vasilache SmallVector<int64_t> hoistPaddings = 66318b92c66SNicolas Vasilache extractFromI64ArrayAttr(getHoistPaddings()); 6645cde5a57SAlex Zinenko if (any_of(hoistPaddings, 6655cde5a57SAlex Zinenko [](int64_t hoistPadding) { return hoistPadding < 0; })) { 6665cde5a57SAlex Zinenko return emitOpError() 6675cde5a57SAlex Zinenko << "expects hoist_paddings to contain positive integers, found " 6685cde5a57SAlex Zinenko << getHoistPaddings(); 6695cde5a57SAlex Zinenko } 6705cde5a57SAlex Zinenko 6715cde5a57SAlex Zinenko ArrayAttr transposes = getTransposePaddings(); 6725cde5a57SAlex Zinenko for (Attribute attr : transposes) { 6735cde5a57SAlex Zinenko SmallVector<int64_t> transpose = extractFromI64ArrayAttr(attr); 6745cde5a57SAlex Zinenko auto sequence = llvm::to_vector(llvm::seq<int64_t>(0, transpose.size())); 6755cde5a57SAlex Zinenko if (!std::is_permutation(sequence.begin(), sequence.end(), 6765cde5a57SAlex Zinenko transpose.begin(), transpose.end())) { 6775cde5a57SAlex Zinenko return emitOpError() 6785cde5a57SAlex Zinenko << "expects transpose_paddings to be a permutation, found " 6795cde5a57SAlex Zinenko << attr; 6805cde5a57SAlex Zinenko } 6815cde5a57SAlex Zinenko } 6825cde5a57SAlex Zinenko return success(); 6835cde5a57SAlex Zinenko } 6845cde5a57SAlex Zinenko 6855cde5a57SAlex Zinenko //===----------------------------------------------------------------------===// 6865a001136SNicolas Vasilache // PromoteOp 6875a001136SNicolas Vasilache //===----------------------------------------------------------------------===// 6885a001136SNicolas Vasilache 6895a001136SNicolas Vasilache DiagnosedSilenceableFailure 6905a001136SNicolas Vasilache transform::PromoteOp::applyToOne(linalg::LinalgOp target, 6915a001136SNicolas Vasilache SmallVectorImpl<Operation *> &results, 6925a001136SNicolas Vasilache transform::TransformState &state) { 6935a001136SNicolas Vasilache LinalgPromotionOptions promotionOptions; 6945a001136SNicolas Vasilache if (!getOperandsToPromote().empty()) 6955a001136SNicolas Vasilache promotionOptions = promotionOptions.setOperandsToPromote( 6965a001136SNicolas Vasilache extractFromI64ArrayAttr(getOperandsToPromote())); 6975a001136SNicolas Vasilache if (getUseFullTilesByDefault()) 6985a001136SNicolas Vasilache promotionOptions = promotionOptions.setUseFullTileBuffersByDefault( 6995a001136SNicolas Vasilache getUseFullTilesByDefault()); 7005a001136SNicolas Vasilache if (getUseAlloca()) 7015a001136SNicolas Vasilache promotionOptions = promotionOptions.setUseAlloca(getUseAlloca()); 7025a001136SNicolas Vasilache if (!getUseFullTileBuffers().empty()) 7035a001136SNicolas Vasilache promotionOptions = promotionOptions.setUseFullTileBuffers( 7045a001136SNicolas Vasilache llvm::to_vector(getUseFullTileBuffers().getAsValueRange<BoolAttr>())); 7051a5d0076SKazu Hirata if (getAlignment().has_value()) 7065a001136SNicolas Vasilache promotionOptions = promotionOptions.setAlignment(*getAlignment()); 7075a001136SNicolas Vasilache 7085a001136SNicolas Vasilache if (failed(promoteSubviewsPrecondition(target, promotionOptions))) 7095a001136SNicolas Vasilache return DiagnosedSilenceableFailure(reportUnknownTransformError(target)); 7105a001136SNicolas Vasilache 7115a001136SNicolas Vasilache SimpleRewriter rewriter(target->getContext()); 7125a001136SNicolas Vasilache rewriter.setInsertionPoint(target); 7135a001136SNicolas Vasilache FailureOr<LinalgOp> res = promoteSubViews(rewriter, target, promotionOptions); 7145a001136SNicolas Vasilache if (failed(res)) 7155a001136SNicolas Vasilache return DiagnosedSilenceableFailure(reportUnknownTransformError(target)); 7165a001136SNicolas Vasilache results.push_back(target); 7175a001136SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 7185a001136SNicolas Vasilache } 7195a001136SNicolas Vasilache 7205a001136SNicolas Vasilache //===----------------------------------------------------------------------===// 7215cde5a57SAlex Zinenko // ScalarizeOp 7225cde5a57SAlex Zinenko //===----------------------------------------------------------------------===// 7235cde5a57SAlex Zinenko 72452307109SNicolas Vasilache DiagnosedSilenceableFailure 72552307109SNicolas Vasilache transform::ScalarizeOp::applyToOne(linalg::LinalgOp target, 72652307109SNicolas Vasilache SmallVectorImpl<Operation *> &results, 72752307109SNicolas Vasilache transform::TransformState &state) { 7285cde5a57SAlex Zinenko LinalgTilingOptions tilingOptions; 7295cde5a57SAlex Zinenko tilingOptions.scalarizeDynamicDims(); 7305cde5a57SAlex Zinenko // Tiling with "scalarize_dyn_dims" actually sets the same lambda as the tile 7315cde5a57SAlex Zinenko // sizes and asserts that it is not already set. 7325cde5a57SAlex Zinenko SmallVector<int64_t> emptyTileSizes; 7335cde5a57SAlex Zinenko LinalgTilingPattern pattern(getContext(), tilingOptions); 7345cde5a57SAlex Zinenko SimpleRewriter rewriter(getContext()); 7355cde5a57SAlex Zinenko rewriter.setInsertionPoint(target); 7365cde5a57SAlex Zinenko FailureOr<TiledLinalgOp> result = 7375cde5a57SAlex Zinenko pattern.returningMatchAndRewrite(target, rewriter); 7385cde5a57SAlex Zinenko if (failed(result)) 73952307109SNicolas Vasilache return DiagnosedSilenceableFailure(reportUnknownTransformError(target)); 7405cde5a57SAlex Zinenko 74152307109SNicolas Vasilache results.push_back(result->op); 74252307109SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 7435cde5a57SAlex Zinenko } 7445cde5a57SAlex Zinenko 7455cde5a57SAlex Zinenko //===----------------------------------------------------------------------===// 746ff6e5508SAlex Zinenko // SplitOp 747ff6e5508SAlex Zinenko //===----------------------------------------------------------------------===// 748ff6e5508SAlex Zinenko 749ff6e5508SAlex Zinenko DiagnosedSilenceableFailure SplitOp::apply(TransformResults &results, 750ff6e5508SAlex Zinenko TransformState &state) { 751ff6e5508SAlex Zinenko // Collect the dynamic split points if provided. 752ff6e5508SAlex Zinenko ArrayRef<Operation *> payload = state.getPayloadOps(getTarget()); 753ff6e5508SAlex Zinenko SimpleRewriter rewriter(getContext()); 754ff6e5508SAlex Zinenko SmallVector<OpFoldResult> splitPoints; 755ff6e5508SAlex Zinenko splitPoints.reserve(payload.size()); 756ff6e5508SAlex Zinenko if (getDynamicSplitPoint()) { 757ff6e5508SAlex Zinenko auto diag = DiagnosedSilenceableFailure::success(); 758ff6e5508SAlex Zinenko splitPoints = llvm::to_vector(llvm::map_range( 759ff6e5508SAlex Zinenko state.getPayloadOps(getDynamicSplitPoint()), [&](Operation *op) { 760ff6e5508SAlex Zinenko if (op->getNumResults() != 1 || 761ff6e5508SAlex Zinenko !op->getResult(0).getType().isIndex()) { 762ff6e5508SAlex Zinenko diag = emitSilenceableError() 763ff6e5508SAlex Zinenko << "expected dynamic split point handle to point to a " 764ff6e5508SAlex Zinenko "single-result index-typed op"; 765ff6e5508SAlex Zinenko diag.attachNote(op->getLoc()) << "dynamic split point"; 766ff6e5508SAlex Zinenko } 767ff6e5508SAlex Zinenko return OpFoldResult(op->getResult(0)); 768ff6e5508SAlex Zinenko })); 769ff6e5508SAlex Zinenko if (!diag.succeeded()) 770ff6e5508SAlex Zinenko return diag; 771ff6e5508SAlex Zinenko 772ff6e5508SAlex Zinenko if (splitPoints.size() != payload.size()) { 773ff6e5508SAlex Zinenko emitError() << "expected the dynamic split point handle to point to as " 774ff6e5508SAlex Zinenko "many operations (" 775ff6e5508SAlex Zinenko << splitPoints.size() << ") as the target handle (" 776ff6e5508SAlex Zinenko << payload.size() << ")"; 777ff6e5508SAlex Zinenko return DiagnosedSilenceableFailure::definiteFailure(); 778ff6e5508SAlex Zinenko } 779ff6e5508SAlex Zinenko } else { 780ff6e5508SAlex Zinenko splitPoints.resize(payload.size(), 781ff6e5508SAlex Zinenko rewriter.getIndexAttr(getStaticSplitPoint())); 782ff6e5508SAlex Zinenko } 783ff6e5508SAlex Zinenko 784ff6e5508SAlex Zinenko // Split each target operation. 785ff6e5508SAlex Zinenko SmallVector<Operation *> first, second; 786ff6e5508SAlex Zinenko for (const auto &pair : llvm::zip(payload, splitPoints)) { 787ff6e5508SAlex Zinenko Operation *target = std::get<0>(pair); 788ff6e5508SAlex Zinenko auto linalgOp = dyn_cast<LinalgOp>(target); 789ff6e5508SAlex Zinenko if (!linalgOp) { 790ff6e5508SAlex Zinenko auto diag = emitSilenceableError() << "only applies to structured ops"; 791ff6e5508SAlex Zinenko diag.attachNote(target->getLoc()) << "target op"; 792ff6e5508SAlex Zinenko return diag; 793ff6e5508SAlex Zinenko } 794ff6e5508SAlex Zinenko 795ff6e5508SAlex Zinenko if (getDimension() >= linalgOp.getNumLoops()) { 796ff6e5508SAlex Zinenko auto diag = emitSilenceableError() << "dimension " << getDimension() 797ff6e5508SAlex Zinenko << " does not exist in target op"; 798ff6e5508SAlex Zinenko diag.attachNote(target->getLoc()) << "target op"; 799ff6e5508SAlex Zinenko return diag; 800ff6e5508SAlex Zinenko } 801ff6e5508SAlex Zinenko 802ff6e5508SAlex Zinenko rewriter.setInsertionPoint(linalgOp); 803ff6e5508SAlex Zinenko std::tie(first.emplace_back(), second.emplace_back()) = 804ff6e5508SAlex Zinenko linalg::splitOp(rewriter, linalgOp, getDimension(), std::get<1>(pair)); 805ff6e5508SAlex Zinenko } 806ff6e5508SAlex Zinenko 807ff6e5508SAlex Zinenko results.set(getFirst().cast<OpResult>(), first); 808ff6e5508SAlex Zinenko results.set(getSecond().cast<OpResult>(), second); 809ff6e5508SAlex Zinenko return DiagnosedSilenceableFailure::success(); 810ff6e5508SAlex Zinenko } 811ff6e5508SAlex Zinenko 812ff6e5508SAlex Zinenko void SplitOp::getEffects( 813ff6e5508SAlex Zinenko SmallVectorImpl<MemoryEffects::EffectInstance> &effects) { 814e15b855eSAlex Zinenko consumesHandle(getTarget(), effects); 815e15b855eSAlex Zinenko if (getDynamicSplitPoint()) 816e15b855eSAlex Zinenko onlyReadsHandle(getDynamicSplitPoint(), effects); 817e15b855eSAlex Zinenko producesHandle(getResults(), effects); 818e15b855eSAlex Zinenko modifiesPayload(effects); 819ff6e5508SAlex Zinenko } 820ff6e5508SAlex Zinenko 821ff6e5508SAlex Zinenko ParseResult SplitOp::parse(OpAsmParser &parser, OperationState &result) { 822ff6e5508SAlex Zinenko OpAsmParser::UnresolvedOperand target, dynamicSplitPoint; 823ff6e5508SAlex Zinenko IntegerAttr staticSplitPoint; 824ff6e5508SAlex Zinenko auto pdlOperationType = 825ff6e5508SAlex Zinenko pdl::OperationType::get(parser.getBuilder().getContext()); 826ff6e5508SAlex Zinenko if (parser.parseOperand(target) || 827ff6e5508SAlex Zinenko parser.resolveOperand(target, pdlOperationType, result.operands) || 828ff6e5508SAlex Zinenko parser.parseKeyword("after")) 829ff6e5508SAlex Zinenko return failure(); 830ff6e5508SAlex Zinenko 831ff6e5508SAlex Zinenko OptionalParseResult dynamicPointParseResult = 832ff6e5508SAlex Zinenko parser.parseOptionalOperand(dynamicSplitPoint); 833ff6e5508SAlex Zinenko if (!dynamicPointParseResult.hasValue()) { 834ff6e5508SAlex Zinenko int64_t staticSplitPointValue; 835ff6e5508SAlex Zinenko if (failed(parser.parseInteger(staticSplitPointValue))) 836ff6e5508SAlex Zinenko return failure(); 837ff6e5508SAlex Zinenko 838ff6e5508SAlex Zinenko staticSplitPoint = 839ff6e5508SAlex Zinenko parser.getBuilder().getI64IntegerAttr(staticSplitPointValue); 840ff6e5508SAlex Zinenko } else { 841ff6e5508SAlex Zinenko if (failed(*dynamicPointParseResult) || 842ff6e5508SAlex Zinenko parser.resolveOperand(dynamicSplitPoint, pdlOperationType, 843ff6e5508SAlex Zinenko result.operands)) { 844ff6e5508SAlex Zinenko return failure(); 845ff6e5508SAlex Zinenko } 846ff6e5508SAlex Zinenko 847ff6e5508SAlex Zinenko staticSplitPoint = 848ff6e5508SAlex Zinenko parser.getBuilder().getI64IntegerAttr(ShapedType::kDynamicSize); 849ff6e5508SAlex Zinenko } 850ff6e5508SAlex Zinenko 851ff6e5508SAlex Zinenko result.addAttribute( 852ff6e5508SAlex Zinenko SplitOp::getStaticSplitPointAttrName(result.name).getValue(), 853ff6e5508SAlex Zinenko staticSplitPoint); 854ff6e5508SAlex Zinenko if (failed(parser.parseOptionalAttrDict(result.attributes))) 855ff6e5508SAlex Zinenko return failure(); 856ff6e5508SAlex Zinenko 857ff6e5508SAlex Zinenko result.addTypes({pdlOperationType, pdlOperationType}); 858ff6e5508SAlex Zinenko return success(); 859ff6e5508SAlex Zinenko } 860ff6e5508SAlex Zinenko 861ff6e5508SAlex Zinenko void SplitOp::print(OpAsmPrinter &printer) { 862ff6e5508SAlex Zinenko printer << " " << getTarget() << " after "; 863ff6e5508SAlex Zinenko int64_t staticSplitSize = static_cast<int64_t>(getStaticSplitPoint()); 864ff6e5508SAlex Zinenko if (staticSplitSize != ShapedType::kDynamicSize) 865ff6e5508SAlex Zinenko printer << staticSplitSize; 866ff6e5508SAlex Zinenko else 867ff6e5508SAlex Zinenko printer << getDynamicSplitPoint(); 868ff6e5508SAlex Zinenko printer << " "; 869ff6e5508SAlex Zinenko printer.printOptionalAttrDict(getOperation()->getAttrs(), 870ff6e5508SAlex Zinenko {getStaticSplitPointAttrName()}); 871ff6e5508SAlex Zinenko } 872ff6e5508SAlex Zinenko 873ff6e5508SAlex Zinenko LogicalResult SplitOp::verify() { 874ff6e5508SAlex Zinenko if ((static_cast<int64_t>(getStaticSplitPoint()) != 875ff6e5508SAlex Zinenko ShapedType::kDynamicSize) ^ 876ff6e5508SAlex Zinenko (getDynamicSplitPoint() == nullptr)) { 877ff6e5508SAlex Zinenko return emitOpError() 878ff6e5508SAlex Zinenko << "expects either a dynamic or a static split point to be provided"; 879ff6e5508SAlex Zinenko } 880ff6e5508SAlex Zinenko return success(); 881ff6e5508SAlex Zinenko } 882ff6e5508SAlex Zinenko 883ff6e5508SAlex Zinenko //===----------------------------------------------------------------------===// 884f439b319SNicolas Vasilache // SplitReductionOp 885f439b319SNicolas Vasilache //===----------------------------------------------------------------------===// 886f439b319SNicolas Vasilache 88752307109SNicolas Vasilache DiagnosedSilenceableFailure 88852307109SNicolas Vasilache transform::SplitReductionOp::applyToOne(linalg::LinalgOp target, 88952307109SNicolas Vasilache SmallVectorImpl<Operation *> &results, 89052307109SNicolas Vasilache transform::TransformState &state) { 891f439b319SNicolas Vasilache ControlSplitReductionFn splitFn = [&](LinalgOp) { 892f439b319SNicolas Vasilache return std::pair<int64_t, unsigned>(getSplitFactor(), 893f439b319SNicolas Vasilache getInsertSplitDimension()); 894f439b319SNicolas Vasilache }; 895f439b319SNicolas Vasilache SimpleRewriter rewriter(getContext()); 896f439b319SNicolas Vasilache rewriter.setInsertionPoint(target); 897f439b319SNicolas Vasilache FailureOr<SplitReductionResult> splitResult = 898178f9bd6SNicolas Vasilache (getUseScalingAlgorithm()) 899178f9bd6SNicolas Vasilache ? splitReductionByScaling(rewriter, target, splitFn, getUseAlloc()) 900178f9bd6SNicolas Vasilache : splitReduction(rewriter, target, splitFn, getUseAlloc()); 901d5716395SNicolas Vasilache if (failed(splitResult)) 90252307109SNicolas Vasilache return DiagnosedSilenceableFailure(reportUnknownTransformError(target)); 90352307109SNicolas Vasilache 90452307109SNicolas Vasilache results.push_back(splitResult->initOrAlloc); 90552307109SNicolas Vasilache results.push_back(splitResult->fillOp); 90652307109SNicolas Vasilache results.push_back(splitResult->splitLinalgOp); 90752307109SNicolas Vasilache results.push_back(splitResult->resultCombiningLinalgOp); 90852307109SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 909d5716395SNicolas Vasilache } 910d5716395SNicolas Vasilache 911d5716395SNicolas Vasilache //===----------------------------------------------------------------------===// 9123c2a74a3SMatthias Springer // TileOp 9133c2a74a3SMatthias Springer //===----------------------------------------------------------------------===// 9143c2a74a3SMatthias Springer 9151d45282aSAlex Zinenko DiagnosedSilenceableFailure 916e3890b7fSAlex Zinenko transform::TileOp::apply(TransformResults &transformResults, 9173c2a74a3SMatthias Springer TransformState &state) { 9183c2a74a3SMatthias Springer LinalgTilingOptions tilingOptions; 91918b92c66SNicolas Vasilache SmallVector<int64_t> tileSizes = extractFromI64ArrayAttr(getStaticSizes()); 9203c2a74a3SMatthias Springer 9214e4a4c05SAlex Zinenko ArrayRef<Operation *> targets = state.getPayloadOps(getTarget()); 9224e4a4c05SAlex Zinenko SmallVector<ArrayRef<Operation *>> dynamicSizeProducers; 9234e4a4c05SAlex Zinenko dynamicSizeProducers.reserve(getDynamicSizes().size()); 9244e4a4c05SAlex Zinenko for (Value dynamicSizeProducerHandle : getDynamicSizes()) { 9254e4a4c05SAlex Zinenko dynamicSizeProducers.push_back( 9264e4a4c05SAlex Zinenko state.getPayloadOps(dynamicSizeProducerHandle)); 9274e4a4c05SAlex Zinenko 9284e4a4c05SAlex Zinenko if (dynamicSizeProducers.back().size() != targets.size()) { 9294e4a4c05SAlex Zinenko DiagnosedSilenceableFailure diag = 9304e4a4c05SAlex Zinenko emitSilenceableError() 9314e4a4c05SAlex Zinenko << "expected as many dynamic size-producing operations (" 9324e4a4c05SAlex Zinenko << dynamicSizeProducers.back().size() << ") as target ops (" 9334e4a4c05SAlex Zinenko << targets.size() << ")"; 9344e4a4c05SAlex Zinenko diag.attachNote(dynamicSizeProducerHandle.getLoc()) << "for this handle"; 9354e4a4c05SAlex Zinenko return diag; 9364e4a4c05SAlex Zinenko } 9374e4a4c05SAlex Zinenko 9384e4a4c05SAlex Zinenko for (Operation *op : dynamicSizeProducers.back()) { 9394e4a4c05SAlex Zinenko if (op->getNumResults() == 1 && 9404e4a4c05SAlex Zinenko op->getResult(0).getType().isa<IndexType>()) 9414e4a4c05SAlex Zinenko continue; 9424e4a4c05SAlex Zinenko DiagnosedSilenceableFailure diag = 9434e4a4c05SAlex Zinenko emitSilenceableError() << "expected sizes to be produced by ops " 9444e4a4c05SAlex Zinenko "with a single index-type result"; 9454e4a4c05SAlex Zinenko diag.attachNote(op->getLoc()) << "size producer op"; 9464e4a4c05SAlex Zinenko diag.attachNote(dynamicSizeProducerHandle.getLoc()) << "for this handle"; 9474e4a4c05SAlex Zinenko return diag; 9484e4a4c05SAlex Zinenko } 9494e4a4c05SAlex Zinenko } 9504e4a4c05SAlex Zinenko 9514e4a4c05SAlex Zinenko SmallVector<Operation *> tiled; 9524e4a4c05SAlex Zinenko SmallVector<SmallVector<Operation *, 4>, 4> loops; 9534e4a4c05SAlex Zinenko loops.resize(getLoops().size()); 9544e4a4c05SAlex Zinenko for (auto &en : llvm::enumerate(targets)) { 9554e4a4c05SAlex Zinenko auto linalgOp = dyn_cast<LinalgOp>(en.value()); 9564e4a4c05SAlex Zinenko if (!linalgOp) { 9574e4a4c05SAlex Zinenko DiagnosedSilenceableFailure diag = emitSilenceableError() 9584e4a4c05SAlex Zinenko << "only linalg ops are supported"; 9594e4a4c05SAlex Zinenko diag.attachNote(en.value()->getLoc()) << "target op"; 9604e4a4c05SAlex Zinenko return diag; 9614e4a4c05SAlex Zinenko } 9624e4a4c05SAlex Zinenko 9634e4a4c05SAlex Zinenko unsigned index = en.index(); 9644e4a4c05SAlex Zinenko if (!tileSizes.empty()) { 9654e4a4c05SAlex Zinenko tilingOptions.setTileSizeComputationFunction( 9664e4a4c05SAlex Zinenko [&, index](OpBuilder &b, Operation *) { 9674e4a4c05SAlex Zinenko SmallVector<Value, 4> sizes; 9684e4a4c05SAlex Zinenko sizes.reserve(tileSizes.size()); 9694e4a4c05SAlex Zinenko unsigned dynamicIdx = 0; 9704e4a4c05SAlex Zinenko for (OpFoldResult ofr : getMixedSizes()) { 9714e4a4c05SAlex Zinenko if (auto attr = ofr.dyn_cast<Attribute>()) { 9724e4a4c05SAlex Zinenko sizes.push_back(b.create<arith::ConstantIndexOp>( 9734e4a4c05SAlex Zinenko getLoc(), attr.cast<IntegerAttr>().getInt())); 9744e4a4c05SAlex Zinenko } else { 9754e4a4c05SAlex Zinenko sizes.push_back( 9764e4a4c05SAlex Zinenko dynamicSizeProducers[dynamicIdx++][index]->getResult(0)); 9774e4a4c05SAlex Zinenko } 9784e4a4c05SAlex Zinenko } 9794e4a4c05SAlex Zinenko return sizes; 9804e4a4c05SAlex Zinenko }); 9814e4a4c05SAlex Zinenko } 9824e4a4c05SAlex Zinenko 9833c2a74a3SMatthias Springer tilingOptions.setInterchange(extractUIntArray(getInterchange())); 9843c2a74a3SMatthias Springer LinalgTilingPattern pattern(getContext(), tilingOptions); 9853c2a74a3SMatthias Springer SimpleRewriter rewriter(linalgOp.getContext()); 9864e4a4c05SAlex Zinenko FailureOr<TiledLinalgOp> tiledOp = 9874e4a4c05SAlex Zinenko pattern.returningMatchAndRewrite(linalgOp, rewriter); 9884e4a4c05SAlex Zinenko if (failed(tiledOp)) 9894e4a4c05SAlex Zinenko return DiagnosedSilenceableFailure::definiteFailure(); 9904e4a4c05SAlex Zinenko 9914e4a4c05SAlex Zinenko tiled.push_back(tiledOp->op); 9924e4a4c05SAlex Zinenko for (const auto &en2 : llvm::enumerate(tiledOp->loops)) 9934e4a4c05SAlex Zinenko loops[en2.index()].push_back(en2.value()); 9944e4a4c05SAlex Zinenko } 9954e4a4c05SAlex Zinenko 9964e4a4c05SAlex Zinenko transformResults.set(getTiledLinalgOp().cast<OpResult>(), tiled); 9974e4a4c05SAlex Zinenko for (const auto &en : llvm::enumerate(loops)) 9984e4a4c05SAlex Zinenko transformResults.set(getLoops()[en.index()].cast<OpResult>(), en.value()); 9994e4a4c05SAlex Zinenko 10004e4a4c05SAlex Zinenko return DiagnosedSilenceableFailure::success(); 10014e4a4c05SAlex Zinenko } 10024e4a4c05SAlex Zinenko 10034e4a4c05SAlex Zinenko SmallVector<OpFoldResult> transform::TileOp::getMixedSizes() { 10044e4a4c05SAlex Zinenko ValueRange dynamic = getDynamicSizes(); 100518b92c66SNicolas Vasilache SmallVector<int64_t> tileSizes = extractFromI64ArrayAttr(getStaticSizes()); 10064e4a4c05SAlex Zinenko SmallVector<OpFoldResult> results; 10074e4a4c05SAlex Zinenko results.reserve(tileSizes.size()); 10084e4a4c05SAlex Zinenko unsigned dynamicPos = 0; 10094e4a4c05SAlex Zinenko Builder builder(getContext()); 10104e4a4c05SAlex Zinenko for (int64_t size : tileSizes) { 10114e4a4c05SAlex Zinenko if (size == ShapedType::kDynamicSize) { 10124e4a4c05SAlex Zinenko results.push_back(dynamic[dynamicPos++]); 10134e4a4c05SAlex Zinenko } else { 10144e4a4c05SAlex Zinenko results.push_back(builder.getIndexAttr(size)); 10154e4a4c05SAlex Zinenko } 10164e4a4c05SAlex Zinenko } 10174e4a4c05SAlex Zinenko return results; 10183c2a74a3SMatthias Springer } 10193c2a74a3SMatthias Springer 10203c2a74a3SMatthias Springer ParseResult transform::TileOp::parse(OpAsmParser &parser, 10213c2a74a3SMatthias Springer OperationState &result) { 10224e4a4c05SAlex Zinenko OpAsmParser::UnresolvedOperand target; 10234e4a4c05SAlex Zinenko SmallVector<OpAsmParser::UnresolvedOperand> dynamicSizes; 10244e4a4c05SAlex Zinenko ArrayAttr staticSizes; 10254e4a4c05SAlex Zinenko auto pdlOperationType = pdl::OperationType::get(parser.getContext()); 10264e4a4c05SAlex Zinenko if (parser.parseOperand(target) || 10274e4a4c05SAlex Zinenko parser.resolveOperand(target, pdlOperationType, result.operands) || 10284e4a4c05SAlex Zinenko parseOperandsOrIntegersSizesList(parser, dynamicSizes, staticSizes) || 10294e4a4c05SAlex Zinenko parser.resolveOperands(dynamicSizes, pdlOperationType, result.operands) || 10304e4a4c05SAlex Zinenko parser.parseOptionalAttrDict(result.attributes)) 10314e4a4c05SAlex Zinenko return ParseResult::failure(); 10324e4a4c05SAlex Zinenko 10334e4a4c05SAlex Zinenko result.addAttribute(getStaticSizesAttrName(result.name), staticSizes); 10344e4a4c05SAlex Zinenko size_t numExpectedLoops = 103518b92c66SNicolas Vasilache staticSizes.size() - llvm::count(extractFromI64ArrayAttr(staticSizes), 0); 10364e4a4c05SAlex Zinenko result.addTypes(SmallVector<Type>(numExpectedLoops + 1, pdlOperationType)); 10374e4a4c05SAlex Zinenko return success(); 10383c2a74a3SMatthias Springer } 10393c2a74a3SMatthias Springer 10403c2a74a3SMatthias Springer void TileOp::print(OpAsmPrinter &p) { 10414e4a4c05SAlex Zinenko p << ' ' << getTarget(); 10424e4a4c05SAlex Zinenko printOperandsOrIntegersSizesList(p, getOperation(), getDynamicSizes(), 10434e4a4c05SAlex Zinenko getStaticSizes()); 10444e4a4c05SAlex Zinenko p.printOptionalAttrDict((*this)->getAttrs(), {getStaticSizesAttrName()}); 10454e4a4c05SAlex Zinenko } 10464e4a4c05SAlex Zinenko 10474e4a4c05SAlex Zinenko void transform::TileOp::getEffects( 10484e4a4c05SAlex Zinenko SmallVectorImpl<MemoryEffects::EffectInstance> &effects) { 10494e4a4c05SAlex Zinenko consumesHandle(getTarget(), effects); 10504e4a4c05SAlex Zinenko onlyReadsHandle(getDynamicSizes(), effects); 10514e4a4c05SAlex Zinenko producesHandle(getTiledLinalgOp(), effects); 10524e4a4c05SAlex Zinenko producesHandle(getLoops(), effects); 10534e4a4c05SAlex Zinenko modifiesPayload(effects); 10543c2a74a3SMatthias Springer } 10553c2a74a3SMatthias Springer 10563c2a74a3SMatthias Springer //===----------------------------------------------------------------------===// 105718b92c66SNicolas Vasilache // TileToForeachThreadOp 105818b92c66SNicolas Vasilache //===----------------------------------------------------------------------===// 105918b92c66SNicolas Vasilache 106018b92c66SNicolas Vasilache DiagnosedSilenceableFailure transform::TileToForeachThreadOp::applyToOne( 106118b92c66SNicolas Vasilache TilingInterface target, SmallVectorImpl<Operation *> &results, 106218b92c66SNicolas Vasilache transform::TransformState &state) { 106318b92c66SNicolas Vasilache IRRewriter rewriter(getContext()); 106418b92c66SNicolas Vasilache rewriter.setInsertionPoint(target); 106518b92c66SNicolas Vasilache auto maybeThreadDimMappingAttr = getThreadDimMapping(); 1066297ba167SChristopher Bate auto dimMapping = 1067297ba167SChristopher Bate llvm::to_vector(maybeThreadDimMappingAttr 106818b92c66SNicolas Vasilache ? extractFromI64ArrayAttr(*maybeThreadDimMappingAttr) 106918b92c66SNicolas Vasilache : ArrayRef<int64_t>{}); 1070297ba167SChristopher Bate 1071297ba167SChristopher Bate FailureOr<ForeachThreadTilingResult> tilingResult = failure(); 1072297ba167SChristopher Bate if (Optional<ArrayAttr> numThreads = getNumThreads()) 1073297ba167SChristopher Bate tilingResult = linalg::tileToForeachThreadOp( 1074297ba167SChristopher Bate rewriter, target, getAsOpFoldResult(*numThreads), dimMapping); 1075297ba167SChristopher Bate 1076297ba167SChristopher Bate if (Optional<ArrayAttr> tileSizes = getTileSizes()) 1077297ba167SChristopher Bate tilingResult = linalg::tileToForeachThreadOpUsingTileSizes( 1078297ba167SChristopher Bate rewriter, target, getAsOpFoldResult(*tileSizes), dimMapping); 1079297ba167SChristopher Bate 108018b92c66SNicolas Vasilache if (failed(tilingResult)) 108118b92c66SNicolas Vasilache return emitDefaultSilenceableFailure(target); 108218b92c66SNicolas Vasilache rewriter.replaceOp(target, tilingResult->tileOp->getResults()); 108318b92c66SNicolas Vasilache results.assign({tilingResult->tileOp, tilingResult->tiledOp}); 108418b92c66SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 108518b92c66SNicolas Vasilache } 108618b92c66SNicolas Vasilache 108718b92c66SNicolas Vasilache //===----------------------------------------------------------------------===// 1088cc6c1592SAlex Zinenko // VectorizeOp 1089cc6c1592SAlex Zinenko //===----------------------------------------------------------------------===// 1090cc6c1592SAlex Zinenko 109152307109SNicolas Vasilache DiagnosedSilenceableFailure 109252307109SNicolas Vasilache transform::VectorizeOp::applyToOne(Operation *target, 109352307109SNicolas Vasilache SmallVectorImpl<Operation *> &results, 109452307109SNicolas Vasilache transform::TransformState &state) { 1095cc6c1592SAlex Zinenko if (!target->hasTrait<OpTrait::IsIsolatedFromAbove>()) { 109652307109SNicolas Vasilache auto diag = this->emitOpError("requires isolated-from-above targets"); 1097cc6c1592SAlex Zinenko diag.attachNote(target->getLoc()) << "non-isolated target"; 109852307109SNicolas Vasilache return DiagnosedSilenceableFailure::definiteFailure(); 1099cc6c1592SAlex Zinenko } 1100cc6c1592SAlex Zinenko 1101cc6c1592SAlex Zinenko MLIRContext *ctx = getContext(); 1102cc6c1592SAlex Zinenko RewritePatternSet patterns(ctx); 1103cc6c1592SAlex Zinenko patterns.add<LinalgVectorizationPattern>(ctx); 1104cc6c1592SAlex Zinenko 1105cc6c1592SAlex Zinenko vector::populateVectorTransferPermutationMapLoweringPatterns(patterns); 1106cc6c1592SAlex Zinenko vector::populateVectorReductionToContractPatterns(patterns); 1107cc6c1592SAlex Zinenko patterns.add<linalg::LinalgCopyVTRForwardingPattern, 1108cc6c1592SAlex Zinenko linalg::LinalgCopyVTWForwardingPattern>(ctx, 1109cc6c1592SAlex Zinenko /*benefit=*/2); 1110cc6c1592SAlex Zinenko vector::TransferReadOp::getCanonicalizationPatterns(patterns, ctx); 1111cc6c1592SAlex Zinenko vector::TransferWriteOp::getCanonicalizationPatterns(patterns, ctx); 1112cc6c1592SAlex Zinenko if (getVectorizePadding()) 1113cc6c1592SAlex Zinenko linalg::populatePadOpVectorizationPatterns(patterns); 1114cc6c1592SAlex Zinenko 11155f0d4f20SAlex Zinenko if (failed(applyPatternsAndFoldGreedily(target, std::move(patterns)))) 111652307109SNicolas Vasilache return DiagnosedSilenceableFailure(reportUnknownTransformError(target)); 111752307109SNicolas Vasilache 111852307109SNicolas Vasilache results.push_back(target); 111952307109SNicolas Vasilache return DiagnosedSilenceableFailure(success()); 1120cc6c1592SAlex Zinenko } 1121cc6c1592SAlex Zinenko 1122cc6c1592SAlex Zinenko //===----------------------------------------------------------------------===// 11233c2a74a3SMatthias Springer // Transform op registration 11243c2a74a3SMatthias Springer //===----------------------------------------------------------------------===// 11253c2a74a3SMatthias Springer 11263c2a74a3SMatthias Springer namespace { 11273c2a74a3SMatthias Springer /// Registers new ops and declares PDL as dependent dialect since the additional 11283c2a74a3SMatthias Springer /// ops are using PDL types for operands and results. 11293c2a74a3SMatthias Springer class LinalgTransformDialectExtension 11303c2a74a3SMatthias Springer : public transform::TransformDialectExtension< 11313c2a74a3SMatthias Springer LinalgTransformDialectExtension> { 11323c2a74a3SMatthias Springer public: 11333c2a74a3SMatthias Springer LinalgTransformDialectExtension() { 11343963b4d0SAlex Zinenko declareDependentDialect<AffineDialect>(); 11354e4a4c05SAlex Zinenko declareDependentDialect<arith::ArithmeticDialect>(); 11363c2a74a3SMatthias Springer declareDependentDialect<pdl::PDLDialect>(); 11373c2a74a3SMatthias Springer declareDependentDialect<scf::SCFDialect>(); 1138cc6c1592SAlex Zinenko declareDependentDialect<vector::VectorDialect>(); 11393c2a74a3SMatthias Springer registerTransformOps< 11403c2a74a3SMatthias Springer #define GET_OP_LIST 11413c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp.inc" 11423c2a74a3SMatthias Springer >(); 11433c2a74a3SMatthias Springer } 11443c2a74a3SMatthias Springer }; 11453c2a74a3SMatthias Springer } // namespace 11463c2a74a3SMatthias Springer 11471f77f01cSNicolas Vasilache #include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOpsEnums.cpp.inc" 11481f77f01cSNicolas Vasilache 11493c2a74a3SMatthias Springer #define GET_OP_CLASSES 11503c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp.inc" 11513c2a74a3SMatthias Springer 11523c2a74a3SMatthias Springer void mlir::linalg::registerTransformDialectExtension( 11533c2a74a3SMatthias Springer DialectRegistry ®istry) { 11543c2a74a3SMatthias Springer registry.addExtensions<LinalgTransformDialectExtension>(); 11553c2a74a3SMatthias Springer } 1156