1 //===- Utils.cpp - Utilities to support the Linalg dialect ----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements utilities for the Linalg dialect.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "mlir/Dialect/Linalg/Utils/Utils.h"
14 #include "mlir/Dialect/Affine/IR/AffineOps.h"
15 #include "mlir/Dialect/Linalg/IR/LinalgOps.h"
16 #include "mlir/Dialect/Linalg/IR/LinalgTypes.h"
17 #include "mlir/Dialect/SCF/SCF.h"
18 #include "mlir/Dialect/StandardOps/IR/Ops.h"
19 #include "mlir/IR/AffineExpr.h"
20 #include "mlir/IR/AffineMap.h"
21 #include "mlir/IR/Matchers.h"
22 #include "mlir/IR/OpImplementation.h"
23 #include "mlir/Pass/Pass.h"
24 #include "mlir/Transforms/FoldUtils.h"
25 
26 using namespace mlir;
27 using namespace mlir::linalg;
28 using namespace mlir::scf;
29 
30 Optional<RegionMatcher::BinaryOpKind>
31 RegionMatcher::matchAsScalarBinaryOp(GenericOp op) {
32   auto &region = op.region();
33   if (!llvm::hasSingleElement(region))
34     return llvm::None;
35 
36   Block &block = region.front();
37   if (block.getNumArguments() != 2 ||
38       !block.getArgument(0).getType().isSignlessIntOrFloat() ||
39       !block.getArgument(1).getType().isSignlessIntOrFloat())
40     return llvm::None;
41 
42   auto &ops = block.getOperations();
43   if (!llvm::hasSingleElement(block.without_terminator()))
44     return llvm::None;
45 
46   using mlir::matchers::m_Val;
47   auto a = m_Val(block.getArgument(0));
48   auto b = m_Val(block.getArgument(1));
49 
50   auto addPattern = m_Op<linalg::YieldOp>(m_Op<AddIOp>(a, b));
51   if (addPattern.match(&ops.back()))
52     return BinaryOpKind::IAdd;
53 
54   return llvm::None;
55 }
56 
57 static Value emitOrFoldComposedAffineApply(OpBuilder &b, Location loc,
58                                            AffineMap map,
59                                            ArrayRef<Value> operandsRef,
60                                            OperationFolder *folder) {
61   SmallVector<Value, 4> operands(operandsRef.begin(), operandsRef.end());
62   fullyComposeAffineMapAndOperands(&map, &operands);
63   canonicalizeMapAndOperands(&map, &operands);
64   return folder ? folder->create<AffineApplyOp>(b, loc, map, operands)
65                 : b.create<AffineApplyOp>(loc, map, operands);
66 }
67 
68 SmallVector<Value, 4> mlir::linalg::applyMapToValues(OpBuilder &b, Location loc,
69                                                      AffineMap map,
70                                                      ArrayRef<Value> values,
71                                                      OperationFolder *folder) {
72   SmallVector<Value, 4> res;
73   res.reserve(map.getNumResults());
74   unsigned numDims = map.getNumDims();
75   // For each `expr` in `map`, applies the `expr` to the values extracted from
76   // ranges. If the resulting application can be folded into a Value, the
77   // folding occurs eagerly. Otherwise, an affine.apply operation is emitted.
78   for (auto expr : map.getResults()) {
79     AffineMap map = AffineMap::get(numDims, 0, expr);
80     res.push_back(emitOrFoldComposedAffineApply(b, loc, map, values, folder));
81   }
82   return res;
83 }
84 
85 /// Returns all the operands of `linalgOp` that are not views.
86 /// Asserts that these operands are value types to allow transformations like
87 /// tiling to just use the values when cloning `linalgOp`.
88 SmallVector<Value, 4>
89 mlir::linalg::getAssumedNonViewOperands(LinalgOp linalgOp) {
90   auto *op = linalgOp.getOperation();
91   unsigned numViews = linalgOp.getNumInputsAndOutputs();
92   unsigned nOperands = op->getNumOperands() - numViews;
93   SmallVector<Value, 4> res;
94   res.reserve(nOperands);
95   for (unsigned i = 0; i < nOperands; ++i) {
96     res.push_back(op->getOperand(numViews + i));
97     auto t = res.back().getType();
98     (void)t;
99     assert((t.isSignlessIntOrIndexOrFloat() || t.isa<VectorType>()) &&
100            "expected scalar or vector type");
101   }
102   return res;
103 }
104