1*3c2a74a3SMatthias Springer //===- LinalgTransformOps.cpp - Implementation of Linalg transform ops ----===// 2*3c2a74a3SMatthias Springer // 3*3c2a74a3SMatthias Springer // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*3c2a74a3SMatthias Springer // See https://llvm.org/LICENSE.txt for license information. 5*3c2a74a3SMatthias Springer // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*3c2a74a3SMatthias Springer // 7*3c2a74a3SMatthias Springer //===----------------------------------------------------------------------===// 8*3c2a74a3SMatthias Springer 9*3c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.h" 10*3c2a74a3SMatthias Springer 11*3c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/IR/Linalg.h" 12*3c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/Transforms/Transforms.h" 13*3c2a74a3SMatthias Springer #include "mlir/Dialect/PDL/IR/PDL.h" 14*3c2a74a3SMatthias Springer #include "mlir/Dialect/PDL/IR/PDLTypes.h" 15*3c2a74a3SMatthias Springer #include "mlir/Dialect/Transform/IR/TransformDialect.h" 16*3c2a74a3SMatthias Springer #include "mlir/Interfaces/SideEffectInterfaces.h" 17*3c2a74a3SMatthias Springer #include "mlir/Parser/Parser.h" 18*3c2a74a3SMatthias Springer #include "llvm/Support/FormatVariadic.h" 19*3c2a74a3SMatthias Springer 20*3c2a74a3SMatthias Springer using namespace mlir; 21*3c2a74a3SMatthias Springer using namespace mlir::linalg; 22*3c2a74a3SMatthias Springer using namespace mlir::transform; 23*3c2a74a3SMatthias Springer 24*3c2a74a3SMatthias Springer /// Extracts a vector of int64_t from an array attribute. Asserts if the 25*3c2a74a3SMatthias Springer /// attribute contains values other than integers. 26*3c2a74a3SMatthias Springer static SmallVector<int64_t> extractI64Array(ArrayAttr attr) { 27*3c2a74a3SMatthias Springer SmallVector<int64_t> result; 28*3c2a74a3SMatthias Springer result.reserve(attr.size()); 29*3c2a74a3SMatthias Springer for (APInt value : attr.getAsValueRange<IntegerAttr>()) 30*3c2a74a3SMatthias Springer result.push_back(value.getSExtValue()); 31*3c2a74a3SMatthias Springer return result; 32*3c2a74a3SMatthias Springer } 33*3c2a74a3SMatthias Springer 34*3c2a74a3SMatthias Springer /// Extracts a vector of unsigned from an array attribute. Asserts if the 35*3c2a74a3SMatthias Springer /// attribute contains values other than intergers. May truncate. 36*3c2a74a3SMatthias Springer static SmallVector<unsigned> extractUIntArray(ArrayAttr attr) { 37*3c2a74a3SMatthias Springer SmallVector<unsigned> result; 38*3c2a74a3SMatthias Springer result.reserve(attr.size()); 39*3c2a74a3SMatthias Springer for (APInt value : attr.getAsValueRange<IntegerAttr>()) 40*3c2a74a3SMatthias Springer result.push_back(value.getZExtValue()); 41*3c2a74a3SMatthias Springer return result; 42*3c2a74a3SMatthias Springer } 43*3c2a74a3SMatthias Springer 44*3c2a74a3SMatthias Springer namespace { 45*3c2a74a3SMatthias Springer /// A simple pattern rewriter that implements no special logic. 46*3c2a74a3SMatthias Springer class SimpleRewriter : public PatternRewriter { 47*3c2a74a3SMatthias Springer public: 48*3c2a74a3SMatthias Springer SimpleRewriter(MLIRContext *context) : PatternRewriter(context) {} 49*3c2a74a3SMatthias Springer }; 50*3c2a74a3SMatthias Springer } // namespace 51*3c2a74a3SMatthias Springer 52*3c2a74a3SMatthias Springer //===----------------------------------------------------------------------===// 53*3c2a74a3SMatthias Springer // TileOp 54*3c2a74a3SMatthias Springer //===----------------------------------------------------------------------===// 55*3c2a74a3SMatthias Springer 56*3c2a74a3SMatthias Springer /// Apply a tiling transformation to all payload ops and store both the 57*3c2a74a3SMatthias Springer /// tiled operation as well as the created tile loops. 58*3c2a74a3SMatthias Springer static LogicalResult 59*3c2a74a3SMatthias Springer applyTilingToAll(Operation *transformOp, Value target, 60*3c2a74a3SMatthias Springer ArrayRef<int64_t> tileSizes, 61*3c2a74a3SMatthias Springer transform::TransformResults &transformResults, 62*3c2a74a3SMatthias Springer transform::TransformState &state, 63*3c2a74a3SMatthias Springer function_ref<FailureOr<TiledLinalgOp>(LinalgOp)> applyFn) { 64*3c2a74a3SMatthias Springer // Number of loops: Number of tiles sizes that are not zero. 65*3c2a74a3SMatthias Springer size_t numLoops = tileSizes.size() - llvm::count(tileSizes, 0); 66*3c2a74a3SMatthias Springer // All payload ops. These should all be LinalgOps for now. 67*3c2a74a3SMatthias Springer ArrayRef<Operation *> payloadOps = state.getPayloadOps(target); 68*3c2a74a3SMatthias Springer 69*3c2a74a3SMatthias Springer SmallVector<Operation *> tiledLinalgOps; 70*3c2a74a3SMatthias Springer SmallVector<SmallVector<Operation *>> loopOps(numLoops); 71*3c2a74a3SMatthias Springer for (unsigned int i = 0; i < numLoops; ++i) 72*3c2a74a3SMatthias Springer loopOps[i].reserve(payloadOps.size()); 73*3c2a74a3SMatthias Springer 74*3c2a74a3SMatthias Springer for (Operation *target : payloadOps) { 75*3c2a74a3SMatthias Springer auto linalgOp = dyn_cast<linalg::LinalgOp>(target); 76*3c2a74a3SMatthias Springer if (!linalgOp) 77*3c2a74a3SMatthias Springer return transformOp->emitError("only LinalgOps are supported"); 78*3c2a74a3SMatthias Springer 79*3c2a74a3SMatthias Springer FailureOr<TiledLinalgOp> tiled = applyFn(linalgOp); 80*3c2a74a3SMatthias Springer if (failed(tiled)) 81*3c2a74a3SMatthias Springer return failure(); 82*3c2a74a3SMatthias Springer 83*3c2a74a3SMatthias Springer tiledLinalgOps.push_back(tiled->op); 84*3c2a74a3SMatthias Springer if (tiled->loops.size() != numLoops) 85*3c2a74a3SMatthias Springer // Not enough loops were generated. This usually means that the input size 86*3c2a74a3SMatthias Springer // was smaller than the tiling size. 87*3c2a74a3SMatthias Springer // TODO: LinalgTilingPattern should return failure(). 88*3c2a74a3SMatthias Springer return failure(); 89*3c2a74a3SMatthias Springer for (unsigned int i = 0; i < numLoops; ++i) 90*3c2a74a3SMatthias Springer loopOps[i].push_back(tiled->loops[i]); 91*3c2a74a3SMatthias Springer } 92*3c2a74a3SMatthias Springer 93*3c2a74a3SMatthias Springer transformResults.set(transformOp->getOpResult(0), tiledLinalgOps); 94*3c2a74a3SMatthias Springer for (unsigned int i = 0; i < numLoops; ++i) 95*3c2a74a3SMatthias Springer transformResults.set(transformOp->getOpResult(i + 1), loopOps[i]); 96*3c2a74a3SMatthias Springer return success(); 97*3c2a74a3SMatthias Springer } 98*3c2a74a3SMatthias Springer 99*3c2a74a3SMatthias Springer LogicalResult transform::TileOp::apply(TransformResults &transformResults, 100*3c2a74a3SMatthias Springer TransformState &state) { 101*3c2a74a3SMatthias Springer LinalgTilingOptions tilingOptions; 102*3c2a74a3SMatthias Springer SmallVector<int64_t> tileSizes = extractI64Array(getSizes()); 103*3c2a74a3SMatthias Springer 104*3c2a74a3SMatthias Springer if (!tileSizes.empty()) 105*3c2a74a3SMatthias Springer tilingOptions.setTileSizes(tileSizes); 106*3c2a74a3SMatthias Springer tilingOptions.setInterchange(extractUIntArray(getInterchange())); 107*3c2a74a3SMatthias Springer LinalgTilingPattern pattern(getContext(), tilingOptions); 108*3c2a74a3SMatthias Springer 109*3c2a74a3SMatthias Springer return applyTilingToAll(getOperation(), getTarget(), tileSizes, 110*3c2a74a3SMatthias Springer transformResults, state, [&](LinalgOp linalgOp) { 111*3c2a74a3SMatthias Springer SimpleRewriter rewriter(linalgOp.getContext()); 112*3c2a74a3SMatthias Springer return pattern.returningMatchAndRewrite(linalgOp, 113*3c2a74a3SMatthias Springer rewriter); 114*3c2a74a3SMatthias Springer }); 115*3c2a74a3SMatthias Springer } 116*3c2a74a3SMatthias Springer 117*3c2a74a3SMatthias Springer ParseResult transform::TileOp::parse(OpAsmParser &parser, 118*3c2a74a3SMatthias Springer OperationState &result) { 119*3c2a74a3SMatthias Springer StringRef sizesAttrName = TileOp::getSizesAttrName(result.name).getValue(); 120*3c2a74a3SMatthias Springer OpAsmParser::UnresolvedOperand targetOperand; 121*3c2a74a3SMatthias Springer SMLoc opLoc; 122*3c2a74a3SMatthias Springer parser.getCurrentLocation(&opLoc); 123*3c2a74a3SMatthias Springer if (parser.parseOperand(targetOperand)) 124*3c2a74a3SMatthias Springer return parser.emitError(opLoc, "expected 'target' operand"); 125*3c2a74a3SMatthias Springer if (parser.parseOptionalAttrDict(result.attributes)) 126*3c2a74a3SMatthias Springer return failure(); 127*3c2a74a3SMatthias Springer Attribute sizesAttr = result.attributes.get(sizesAttrName); 128*3c2a74a3SMatthias Springer if (!sizesAttr) 129*3c2a74a3SMatthias Springer return parser.emitError(opLoc) 130*3c2a74a3SMatthias Springer << "expected '" << sizesAttrName << "' attribute"; 131*3c2a74a3SMatthias Springer auto sizesArrayAttr = sizesAttr.dyn_cast<ArrayAttr>(); 132*3c2a74a3SMatthias Springer if (!sizesArrayAttr) 133*3c2a74a3SMatthias Springer return parser.emitError(opLoc) 134*3c2a74a3SMatthias Springer << "'" << sizesAttrName << "' attribute must be an array"; 135*3c2a74a3SMatthias Springer Type pdlOpType = parser.getBuilder().getType<pdl::OperationType>(); 136*3c2a74a3SMatthias Springer size_t numExpectedLoops = 137*3c2a74a3SMatthias Springer sizesArrayAttr.size() - llvm::count(extractI64Array(sizesArrayAttr), 0); 138*3c2a74a3SMatthias Springer result.addTypes(SmallVector<Type>(numExpectedLoops + 1, pdlOpType)); 139*3c2a74a3SMatthias Springer if (parser.resolveOperand(targetOperand, pdlOpType, result.operands)) 140*3c2a74a3SMatthias Springer return failure(); 141*3c2a74a3SMatthias Springer return success(); 142*3c2a74a3SMatthias Springer } 143*3c2a74a3SMatthias Springer 144*3c2a74a3SMatthias Springer void TileOp::print(OpAsmPrinter &p) { 145*3c2a74a3SMatthias Springer p << ' '; 146*3c2a74a3SMatthias Springer p << getTarget(); 147*3c2a74a3SMatthias Springer p.printOptionalAttrDict((*this)->getAttrs()); 148*3c2a74a3SMatthias Springer } 149*3c2a74a3SMatthias Springer 150*3c2a74a3SMatthias Springer void TileOp::getEffects( 151*3c2a74a3SMatthias Springer SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>> 152*3c2a74a3SMatthias Springer &effects) { 153*3c2a74a3SMatthias Springer // `target` arg is consumed and can no longer be used. 154*3c2a74a3SMatthias Springer effects.emplace_back(MemoryEffects::Read::get(), getTarget(), 155*3c2a74a3SMatthias Springer TransformMappingResource::get()); 156*3c2a74a3SMatthias Springer effects.emplace_back(MemoryEffects::Free::get(), getTarget(), 157*3c2a74a3SMatthias Springer TransformMappingResource::get()); 158*3c2a74a3SMatthias Springer 159*3c2a74a3SMatthias Springer for (Value r : getResults()) { 160*3c2a74a3SMatthias Springer effects.emplace_back(MemoryEffects::Write::get(), r, 161*3c2a74a3SMatthias Springer TransformMappingResource::get()); 162*3c2a74a3SMatthias Springer effects.emplace_back(MemoryEffects::Allocate::get(), r, 163*3c2a74a3SMatthias Springer TransformMappingResource::get()); 164*3c2a74a3SMatthias Springer } 165*3c2a74a3SMatthias Springer 166*3c2a74a3SMatthias Springer effects.emplace_back(MemoryEffects::Read::get(), PayloadIRResource::get()); 167*3c2a74a3SMatthias Springer effects.emplace_back(MemoryEffects::Write::get(), PayloadIRResource::get()); 168*3c2a74a3SMatthias Springer } 169*3c2a74a3SMatthias Springer 170*3c2a74a3SMatthias Springer //===----------------------------------------------------------------------===// 171*3c2a74a3SMatthias Springer // Transform op registration 172*3c2a74a3SMatthias Springer //===----------------------------------------------------------------------===// 173*3c2a74a3SMatthias Springer 174*3c2a74a3SMatthias Springer namespace { 175*3c2a74a3SMatthias Springer /// Registers new ops and declares PDL as dependent dialect since the additional 176*3c2a74a3SMatthias Springer /// ops are using PDL types for operands and results. 177*3c2a74a3SMatthias Springer class LinalgTransformDialectExtension 178*3c2a74a3SMatthias Springer : public transform::TransformDialectExtension< 179*3c2a74a3SMatthias Springer LinalgTransformDialectExtension> { 180*3c2a74a3SMatthias Springer public: 181*3c2a74a3SMatthias Springer LinalgTransformDialectExtension() { 182*3c2a74a3SMatthias Springer declareDependentDialect<pdl::PDLDialect>(); 183*3c2a74a3SMatthias Springer declareDependentDialect<scf::SCFDialect>(); 184*3c2a74a3SMatthias Springer registerTransformOps< 185*3c2a74a3SMatthias Springer #define GET_OP_LIST 186*3c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp.inc" 187*3c2a74a3SMatthias Springer >(); 188*3c2a74a3SMatthias Springer } 189*3c2a74a3SMatthias Springer }; 190*3c2a74a3SMatthias Springer } // namespace 191*3c2a74a3SMatthias Springer 192*3c2a74a3SMatthias Springer #define GET_OP_CLASSES 193*3c2a74a3SMatthias Springer #include "mlir/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp.inc" 194*3c2a74a3SMatthias Springer 195*3c2a74a3SMatthias Springer void mlir::linalg::registerTransformDialectExtension( 196*3c2a74a3SMatthias Springer DialectRegistry ®istry) { 197*3c2a74a3SMatthias Springer registry.addExtensions<LinalgTransformDialectExtension>(); 198*3c2a74a3SMatthias Springer } 199