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/LoopOps/LoopOps.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/Support/STLExtras.h"
25 #include "mlir/Transforms/FoldUtils.h"
26 
27 using namespace mlir;
28 using namespace mlir::linalg;
29 using namespace mlir::loop;
30 
31 Optional<RegionMatcher::BinaryOpKind>
32 RegionMatcher::matchAsScalarBinaryOp(GenericOp op) {
33   auto &region = op.region();
34   if (!has_single_element(region))
35     return llvm::None;
36 
37   Block &block = region.front();
38   if (block.getNumArguments() != 2 ||
39       !block.getArgument(0).getType().isSignlessIntOrFloat() ||
40       !block.getArgument(1).getType().isSignlessIntOrFloat())
41     return llvm::None;
42 
43   auto &ops = block.getOperations();
44   if (!has_single_element(block.without_terminator()))
45     return llvm::None;
46 
47   using mlir::matchers::m_Val;
48   auto a = m_Val(block.getArgument(0));
49   auto b = m_Val(block.getArgument(1));
50 
51   auto addPattern = m_Op<linalg::YieldOp>(m_Op<AddIOp>(a, b));
52   if (addPattern.match(&ops.back()))
53     return BinaryOpKind::IAdd;
54 
55   return llvm::None;
56 }
57 
58 static Value emitOrFoldComposedAffineApply(OpBuilder &b, Location loc,
59                                            AffineMap map,
60                                            ArrayRef<Value> operandsRef,
61                                            OperationFolder *folder) {
62   SmallVector<Value, 4> operands(operandsRef.begin(), operandsRef.end());
63   fullyComposeAffineMapAndOperands(&map, &operands);
64   canonicalizeMapAndOperands(&map, &operands);
65   return folder ? folder->create<AffineApplyOp>(b, loc, map, operands)
66                 : b.create<AffineApplyOp>(loc, map, operands);
67 }
68 
69 SmallVector<Value, 4> mlir::linalg::applyMapToValues(OpBuilder &b, Location loc,
70                                                      AffineMap map,
71                                                      ArrayRef<Value> values,
72                                                      OperationFolder *folder) {
73   SmallVector<Value, 4> res;
74   res.reserve(map.getNumResults());
75   unsigned numDims = map.getNumDims();
76   // For each `expr` in `map`, applies the `expr` to the values extracted from
77   // ranges. If the resulting application can be folded into a Value, the
78   // folding occurs eagerly. Otherwise, an affine.apply operation is emitted.
79   for (auto expr : map.getResults()) {
80     AffineMap map = AffineMap::get(numDims, 0, expr);
81     res.push_back(emitOrFoldComposedAffineApply(b, loc, map, values, folder));
82   }
83   return res;
84 }
85 
86 /// Returns all the operands of `linalgOp` that are not views.
87 /// Asserts that these operands are value types to allow transformations like
88 /// tiling to just use the values when cloning `linalgOp`.
89 SmallVector<Value, 4>
90 mlir::linalg::getAssumedNonViewOperands(LinalgOp linalgOp) {
91   auto *op = linalgOp.getOperation();
92   unsigned numViews = linalgOp.getNumInputsAndOutputs();
93   unsigned nOperands = op->getNumOperands() - numViews;
94   SmallVector<Value, 4> res;
95   res.reserve(nOperands);
96   for (unsigned i = 0; i < nOperands; ++i) {
97     res.push_back(op->getOperand(numViews + i));
98     auto t = res.back().getType();
99     (void)t;
100     assert((t.isSignlessIntOrIndexOrFloat() || t.isa<VectorType>()) &&
101            "expected scalar or vector type");
102   }
103   return res;
104 }
105