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 &registry) {
197*3c2a74a3SMatthias Springer   registry.addExtensions<LinalgTransformDialectExtension>();
198*3c2a74a3SMatthias Springer }
199