• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//===- LinalgOps.td - Linalg dialect ops -------------------*- tablegen -*-===//
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 is the operation definition file for linear algebra operations.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LINALG_OPS
14#define LINALG_OPS
15
16include "mlir/Dialect/Linalg/IR/LinalgBase.td"
17include "mlir/Interfaces/ControlFlowInterfaces.td"
18include "mlir/Interfaces/SideEffectInterfaces.td"
19include "mlir/Interfaces/ViewLikeInterface.td"
20
21// Base class for Linalg dialect ops that do not correspond to library calls.
22class Linalg_Op<string mnemonic, list<OpTrait> traits = []> :
23    Op<Linalg_Dialect, mnemonic, traits> {
24  // For every linalg op, there needs to be a:
25  //   * void print(OpAsmPrinter &p, ${C++ class of Op} op)
26  //   * LogicalResult verify(${C++ class of Op} op)
27  //   * ParseResult parse${C++ class of Op}(OpAsmParser &parser,
28  //                                         OperationState &result)
29  // functions.
30  let printer = [{ return ::print(p, *this); }];
31  let verifier = [{ return ::verify(*this); }];
32  let parser = [{ return ::parse$cppClass(parser, result); }];
33}
34
35def Linalg_RangeOp :
36    Linalg_Op<"range", [NoSideEffect]>,
37    Arguments<(ins Index:$min, Index:$max, Index:$step)>,
38    Results<(outs Range)> {
39  let summary = "Create a `range` type value, used to create `view`s";
40  let description = [{
41    The `linalg.range` op creates a `!linalg.range` from 3 values of type
42    `index` that represent the min, max and step values of the `range`. This
43    type does not pass function boundaries at the moment.
44
45    Example:
46
47    ```mlir
48    %3 = linalg.range %0:%1:%2 : !linalg.range
49    ````
50  }];
51  let builders = [
52    OpBuilderDAG<(ins "Value":$min, "Value":$max, "Value":$step),
53    [{
54      auto rangeType = RangeType::get($_builder.getContext());
55      build($_builder, $_state, rangeType, min, max, step);
56    }]>];
57
58  // Fully specified by traits.
59  let verifier = ?;
60  let assemblyFormat = "$min `:` $max `:` $step attr-dict `:` type(results)";
61}
62
63class Linalg_ReshapeLikeOp<string mnemonic, list<OpTrait> traits = []> :
64    Linalg_Op<mnemonic, !listconcat(traits, [NoSideEffect])> {
65  let builders = [
66    // Builders for a contracting reshape whose result type is computed from
67    // `src` and `reassociation`.
68    OpBuilderDAG<(ins "Value":$src,
69      "ArrayRef<ReassociationExprs>":$reassociation,
70      CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>,
71    OpBuilderDAG<(ins "Value":$src,
72      "ArrayRef<ReassociationIndices>":$reassociation,
73      CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs),
74    [{
75      auto reassociationMaps =
76          convertReassociationIndicesToMaps($_builder, reassociation);
77      build($_builder, $_state, src, reassociationMaps, attrs);
78    }]>,
79
80    // Builders for a reshape whose result type is passed explicitly. This may
81    // be either a contracting or expanding reshape.
82    OpBuilderDAG<(ins "Type":$resultType, "Value":$src,
83      "ArrayRef<ReassociationExprs>":$reassociation,
84      CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>,
85    OpBuilderDAG<(ins "Type":$resultType, "Value":$src,
86      "ArrayRef<ReassociationIndices>":$reassociation,
87      CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs),
88    [{
89      auto reassociationMaps =
90          convertReassociationIndicesToMaps($_builder, reassociation);
91      build($_builder, $_state, resultType, src, reassociationMaps, attrs);
92    }]>
93  ];
94
95  code commonExtraClassDeclaration = [{
96    static StringRef getReassociationAttrName() { return "reassociation"; }
97    SmallVector<AffineMap, 4> getReassociationMaps() {
98      return llvm::to_vector<4>(llvm::map_range(reassociation(), [
99      ](Attribute a) { return a.cast<AffineMapAttr>().getValue(); }));
100    }
101  }];
102  let assemblyFormat = [{
103    $src $reassociation attr-dict `:` type($src) `into` type(results)
104  }];
105}
106
107def Linalg_ReshapeOp : Linalg_ReshapeLikeOp<"reshape",
108    [DeclareOpInterfaceMethods<ViewLikeOpInterface>]>,
109    Arguments<(ins AnyStridedMemRef:$src, AffineMapArrayAttr:$reassociation)>,
110    Results<(outs AnyStridedMemRef:$result)> {
111  let summary = "linalg.reshape produces a new view into the operand view";
112  let description = [{
113    The `linalg.reshape` op produces a new view whose sizes are a reassociation
114    of the original `view`. Depending on whether or not the reassociated
115    MemRefType is contiguous, the resulting memref may require explicit alloc
116    and copies.
117
118    A reassociation is defined as a continuous grouping of dimensions and is
119    represented with an affine map array attribute. In the future,
120    non-continuous groupings may be allowed (i.e. permutations, reindexings
121    etc).
122
123    For now, it is assumed that either:
124      1. a reassociation produces and consumes contiguous MemRefType or,
125      2. the reshape op will be folded into its consumers (by changing the shape
126         of the computations).
127    All other cases are undefined behavior and a reshape op may not lower to
128    LLVM if it cannot be proven statically that it does not require alloc+copy.
129
130    A reshape may either collapse or expand dimensions, depending on the
131    relationship between source and target memref ranks. The verification rule
132    is that the reassociation maps are applied to the memref with the larger
133    rank to obtain the memref with the smaller rank. In the case of a dimension
134    expansion, the reassociation maps can be interpreted as inverse maps.
135
136    The result memref type of a reshape when dimensions are collapsed
137    (operand memref type when dimensions are expanded) can be
138    zero-ranked if the operand memref type (or the result memref type
139    when dimensions are expanded) is statically shaped with all
140    dimensions being unit extent. In such cases the reassociation map
141    is empty.
142
143    Examples:
144
145    ```mlir
146    // Dimension collapse (i, j) -> i' and k -> k'
147    %1 = linalg.reshape %0 [(i, j, k) -> (i, j), (i, j, k) -> (k)] :
148      memref<?x?x?xf32, stride_spec> into memref<?x?xf32, stride_spec_2>
149    ```
150
151    ```mlir
152    // Dimension expansion i -> (i', j') and (k) -> (k')
153    %1 = linalg.reshape %0 [(i, j, k) -> (i, j), (i, j, k) -> (k)] :
154      memref<?x?xf32, stride_spec> into memref<?x?x?xf32, stride_spec_2>
155    ```
156  }];
157  let extraClassDeclaration = commonExtraClassDeclaration # [{
158    MemRefType getSrcType() { return src().getType().cast<MemRefType>(); }
159    MemRefType getResultType() { return result().getType().cast<MemRefType>(); }
160  }];
161  let hasFolder = 1;
162  let hasCanonicalizer = 1;
163}
164
165def Linalg_TensorReshapeOp : Linalg_ReshapeLikeOp<"tensor_reshape">,
166    Arguments<(ins AnyTensor:$src,
167                   AffineMapArrayAttr:$reassociation)>,
168    Results<(outs AnyTensor:$result)> {
169  let summary = "linalg.tensor_reshape produces a new reshaped tensor.";
170  let description = [{
171    The `linalg.reshape` op produces a new tensor whose sizes are a
172    reassociation of the original `src`.
173
174    A reassociation is defined as a continuous grouping of dimensions and is
175    represented with an affine map array attribute. In the future,
176    non-continuous groupings may be allowed (i.e. permutations, reindexings
177    etc).
178
179    A reshape may either collapse or expand dimensions, depending on the
180    relationship between source and target tensor ranks. The verification rule
181    is that the reassociation maps are applied to the tensor with the larger
182    rank to obtain the tensor with the smaller rank. In the case of a dimension
183    expansion, the reassociation maps can be interpreted as inverse maps.
184
185    The result tensor type of a reshape when dimensions are collapsed
186    (operand tensor type when dimensions are expanded) can be
187    zero-ranked if the operand tensor type (or the result tensor type
188    when dimensions are expanded) is statically shaped with all
189    dimensions being unit extent. In such cases the reassociation map
190    is empty.
191
192    Examples:
193
194    ```mlir
195    // Dimension collapse (i, j) -> i' and k -> k'
196    %b = linalg.tensor_reshape %a [(i, j, k) -> (i, j), (i, j, k) -> (k)] :
197      tensor<?x?x?xf32> into tensor<?x?xf32>
198    ```
199
200    ```mlir
201    // Dimension expansion i -> (i', j') and (k) -> (k')
202    %b = linalg.tensor_reshape %a [(i, j, k) -> (i, j), (i, j, k) -> (k)] :
203      tensor<?x?xf32> into tensor<?x?x?xf32>
204    ```
205  }];
206  let extraClassDeclaration = commonExtraClassDeclaration # [{
207    RankedTensorType getSrcType() {
208      return src().getType().cast<RankedTensorType>();
209    }
210    RankedTensorType getResultType() {
211      return result().getType().cast<RankedTensorType>();
212    }
213  }];
214  let hasFolder = 1;
215  let hasCanonicalizer = 1;
216}
217
218def Linalg_SliceOp : Linalg_Op<"slice", [
219      DeclareOpInterfaceMethods<ViewLikeOpInterface>, NoSideEffect]>,
220    Arguments<(ins AnyStridedMemRef:$view,
221                   Variadic<AnyTypeOf<[Range, Index]>>:$indexings)>,
222    Results<(outs AnyStridedMemRef)> {
223  let summary = "Produce a rank-reduced `subview` of a base `view`.";
224  let description = [{
225    The `linalg.slice` op allows defining a subregion of a smaller rank than the
226    operand `view` within the underlying buffer.
227
228    A `linalg.slice` op takes a view and a variadic number of indexings and
229    produces a `view` of the same elemental type. An indexing is either:
230      1. a `linalg.range`, in which case it does not reduce the rank of the
231         parent `view` along the corresponding dimension.
232      2. an `index`, in which case it reduces the rank of the parent view by
233         one.
234
235    If an indexing extends past the size of the `view`, this is undefined
236    behavior. Ideally the `linalg.slice` operation would automatically truncate
237    it to be within bounds but there are tradeoffs involved now that `std.view`
238    is a standard op.
239
240    Examples:
241
242      1. rank-preserving `slice`:
243
244      ```mlir
245      %4 = linalg.slice %0[%1, %2] : memref<?x?xf32, stride_spec>,
246        !linalg.range, !linalg.range, memref<?x?xf32, stride_spec>
247      ```
248
249      2. rank-reducing `slice` (from 2-D to 1-D):
250
251      ```mlir
252      %4 = linalg.slice %0[%1, %2] : memref<?x?xf32, stride_spec>,
253        index, !linalg.range, memref<?x?xf32, stride_spec>
254      ```
255
256      3. rank-reducing `slice` (from 2-D to 0-D):
257
258      ```mlir
259      %4 = linalg.slice %0[%1, %2] : memref<?x?xf32, stride_spec>,
260        index, index, memref<?x?xf32, stride_spec>
261      ```
262  }];
263
264  let builders = [OpBuilderDAG<(ins "Value":$base, "ValueRange":$indexings)>];
265
266  let extraClassDeclaration = [{
267    enum { FirstIndexingOperand = 1 };
268    unsigned getRank() { return getShapedType().getRank(); }
269    Type getElementType() { return getShapedType().getElementType(); }
270    ShapedType getShapedType() { return getType().cast<ShapedType>(); }
271    unsigned getBaseViewRank() { return getBaseViewType().getRank(); }
272    ShapedType getBaseViewType() { return view().getType().cast<ShapedType>();}
273
274    // Get the underlying indexing at a given rank.
275    Value indexing(unsigned rank) { return *(indexings().begin() + rank); }
276
277    // Get the subset of indexings that are of RangeType.
278    SmallVector<Value, 8> getRanges() {
279      SmallVector<Value, 8> res;
280      for (auto operand : indexings())
281        if (!operand.getType().isa<IndexType>())
282          res.push_back(operand);
283      return res;
284    }
285  }];
286
287  let hasFolder = 1;
288}
289
290def Linalg_YieldOp : Linalg_Op<"yield", [NoSideEffect, ReturnLike, Terminator]>,
291    Arguments<(ins Variadic<AnyType>:$values)> {
292  let summary = "Linalg yield operation";
293  let description = [{
294    `linalg.yield` is a special terminator operation for blocks inside regions
295    in `linalg` generic ops. It returns values to the immediately enclosing
296    `linalg` generic op.
297
298    Example:
299
300    ```mlir
301    linalg.yield %f0, %f1 : f32, f32
302    ```
303  }];
304}
305
306#endif // LINALG_OPS
307