Bump version to 19.1.0 (final)
[llvm-project.git] / mlir / docs / Dialects / Vector.md
blob6d05d9b90467662a250afd37d59f288d7de62dc3
1 # 'vector' Dialect
3 [TOC]
5 MLIR supports multi-dimensional `vector` types and custom operations on those
6 types. A generic, retargetable, higher-order `vector` type (`n-D` with `n > 1`)
7 is a structured type, that carries semantic information useful for
8 transformations. This document discusses retargetable abstractions that exist in
9 MLIR today and operate on ssa-values of type `vector` along with pattern
10 rewrites and lowerings that enable targeting specific instructions on concrete
11 targets. These abstractions serve to separate concerns between operations on
12 `memref` (a.k.a buffers) and operations on `vector` values. This is not a new
13 proposal but rather a textual documentation of existing MLIR components along
14 with a rationale.
16 ## Positioning in the Codegen Infrastructure
18 The following diagram, recently presented with the
19 [StructuredOps abstractions](https://drive.google.com/corp/drive/u/0/folders/1sRAsgsd8Bvpm_IxREmZf2agsGU2KvrK-),
20 captures the current codegen paths implemented in MLIR in the various existing
21 lowering paths.
22 ![](https://user-images.githubusercontent.com/10148468/71177417-f78e4d80-2239-11ea-92ef-700f42ea503f.png)
24 The following diagram seeks to isolate `vector` dialects from the complexity of
25 the codegen paths and focus on the payload-carrying ops that operate on std and
26 `vector` types. This diagram is not to be taken as set in stone and
27 representative of what exists today but rather illustrates the layering of
28 abstractions in MLIR.
30 ![`vector` Abstractions in MLIR](https://user-images.githubusercontent.com/10148468/71176949-e85ad000-2238-11ea-9806-200843bc4943.png)
32 This  separates concerns related to (a) defining efficient operations on
33 `vector` types from (b) program analyses + transformations on `memref`, loops
34 and other types of structured ops (be they `HLO`, `LHLO`, `Linalg` or other ).
35 Looking a bit forward in time, we can put a stake in the ground and venture that
36 the higher level of `vector`-level primitives we build and target from codegen
37 (or some user/language level), the simpler our task will be, the more complex
38 patterns can be expressed and the better performance will be.
40 ## Components of a Generic Retargetable Vector-Level Dialect
42 The existing MLIR `vector`-level dialects are related to the following bottom-up
43 abstractions:
45 1.  Representation in `LLVMIR` via data structures, instructions and intrinsics.
46     This is referred to as the `LLVM` level.
47 2.  Set of machine-specific operations and types that are built to translate
48     almost 1-1 with the HW ISA. This is referred to as the Hardware Vector
49     level; a.k.a `HWV`. For instance, we have (a) the `NVVM` dialect (for
50     `CUDA`) with tensor core ops, (b) accelerator-specific dialects (internal),
51     a potential (future) `CPU` dialect to capture `LLVM` intrinsics more closely
52     and other dialects for specific hardware. Ideally this should be
53     auto-generated as much as possible from the `LLVM` level.
54 3.  Set of virtual, machine-agnostic, operations that are informed by costs at
55     the `HWV`-level. This is referred to as the Virtual Vector level; a.k.a
56     `VV`. This is the level that higher-level abstractions (codegen, automatic
57     vectorization, potential vector language, ...) targets.
59 The existing generic, retargetable, `vector`-level dialect is related to the
60 following top-down rewrites and conversions:
62 1.  MLIR Rewrite Patterns applied by the MLIR `PatternRewrite` infrastructure to
63     progressively lower to implementations that match closer and closer to the
64     `HWV`. Some patterns are "in-dialect" `VV -> VV` and some are conversions
65     `VV -> HWV`.
66 2.  `Virtual Vector -> Hardware Vector` lowering is specified as a set of MLIR
67     lowering patterns that are specified manually for now.
68 3.  `Hardware Vector -> LLVM` lowering is a mechanical process that is written
69     manually at the moment and that should be automated, following the `LLVM ->
70     Hardware Vector` ops generation as closely as possible.
72 ## Short Description of the Existing Infrastructure
74 ### LLVM level
76 On CPU, the `n-D` `vector` type currently lowers to `!llvm<array<vector>>`. More
77 concretely, `vector<4x8x128xf32>` lowers to `!llvm<[4 x [ 8 x [ 128 x float
78 ]]]>`. There are tradeoffs involved related to how one can access subvectors and
79 how one uses `llvm.extractelement`, `llvm.insertelement` and
80 `llvm.shufflevector`. A [deeper dive section](#DeeperDive) discusses the current
81 lowering choices and tradeoffs.
83 ### Hardware Vector Ops
85 Hardware Vector Ops are implemented as one dialect per target. For internal
86 hardware, we are auto-generating the specific HW dialects. For `GPU`, the `NVVM`
87 dialect adds operations such as `mma.sync`, `shfl` and tests. For `CPU` things
88 are somewhat in-flight because the abstraction is close to `LLVMIR`. The jury is
89 still out on  whether a generic `CPU` dialect is concretely needed, but it seems
90 reasonable to have the same levels of abstraction for all targets and perform
91 cost-based lowering decisions in MLIR even for `LLVM`. Specialized `CPU`
92 dialects that would capture specific features not well captured by LLVM peephole
93 optimizations of on different types that core MLIR supports (e.g. Scalable
94 Vectors) are welcome future extensions.
96 ### Virtual Vector Ops
98 Some existing Arith and Vector Dialect on `n-D` `vector` types comprise:
100 ```mlir
101 // Produces a vector<3x7x8xf32>
102 %a = arith.addf %0, %1 : vector<3x7x8xf32>
103 // Produces a vector<3x7x8xf32>
104 %b = arith.mulf %0, %1 : vector<3x7x8xf32>
105 // Produces a vector<3x7x8xf32>
106 %c = vector.splat %1 : vector<3x7x8xf32>
108 %d = vector.extract %0[1]: vector<7x8xf32> from vector<3x7x8xf32>
109 %e = vector.extract %0[1, 5]: vector<8xf32> from vector<3x7x8xf32>
110 %f = vector.outerproduct %0, %1: vector<4xf32>, vector<8xf32>      // -> vector<4x8xf32>
111 %g = vector.outerproduct %0, %1, %2: vector<4xf32>, vector<8xf32>  // fma when adding %2
113 // Returns a slice of type vector<2x2x16xf32>
114 %h = vector.strided_slice %0
115     {offsets = [2, 2], sizes = [2, 2], strides = [1, 1]}:
116   vector<4x8x16xf32>
118 %i = vector.transfer_read %A[%0, %1]
119     {permutation_map = (d0, d1) -> (d0)}:
120   memref<7x?xf32>, vector<4xf32>
122 vector.transfer_write %f1, %A[%i0, %i1, %i2, %i3]
123     {permutation_map = (d0, d1, d2, d3) -> (d3, d1, d0)} :
124   vector<5x4x3xf32>, memref<?x?x?x?xf32>
127 The list of Vector is currently undergoing evolutions and is best kept track of
128 by following the evolution of the
129 [VectorOps.td](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/Vector/IR/VectorOps.td)
130 ODS file (markdown documentation is automatically generated locally when
131 building and populates the
132 [Vector doc](https://github.com/llvm/llvm-project/blob/main/mlir/docs/Dialects/Vector.md)).
133 Recent extensions are driven by concrete use cases of interest. A notable such
134 use case is the `vector.contract` op which applies principles of the
135 StructuredOps abstraction to `vector` types.
137 ### Virtual Vector Rewrite Patterns
139 The following rewrite patterns exist at the `VV->VV` level:
141 1.  The now retired `MaterializeVector` pass used to legalize ops on a
142     coarse-grained virtual `vector` to a finer-grained virtual `vector` by
143     unrolling. This has been rewritten as a retargetable unroll-and-jam pattern
144     on `vector` ops and `vector` types.
145 2.  The lowering of `vector_transfer` ops legalizes `vector` load/store ops to
146     permuted loops over scalar load/stores. This should evolve to loops over
147     `vector` load/stores + `mask` operations as they become available `vector`
148     ops at the `VV` level.
150 The general direction is to add more Virtual Vector level ops and implement more
151 useful `VV -> VV` rewrites as composable patterns that the PatternRewrite
152 infrastructure can apply iteratively.
154 ### Virtual Vector to Hardware Vector Lowering
156 For now, `VV -> HWV` are specified in C++ (see for instance the
157 [SplatOpLowering for n-D vectors](https://github.com/tensorflow/mlir/commit/0a0c4867c6a6fcb0a2f17ef26a791c1d551fe33d)
158 or the
159 [VectorOuterProductOp lowering](https://github.com/tensorflow/mlir/commit/957b1ca9680b4aacabb3a480fbc4ebd2506334b8)).
161 Simple
162 [conversion tests](https://github.com/llvm/llvm-project/blob/main/mlir/test/Conversion/VectorToLLVM/vector-to-llvm.mlir)
163 are available for the `LLVM` target starting from the Virtual Vector Level.
165 ## Rationale
167 ### Hardware as `vector` Machines of Minimum Granularity
169 Higher-dimensional `vector`s are ubiquitous in modern HPC hardware. One way to
170 think about Generic Retargetable `vector`-Level Dialect is that it operates on
171 `vector` types that are multiples of a "good" `vector` size so the HW can
172 efficiently implement a set of high-level primitives (e.g.
173 `vector<8x8x8x16xf32>` when HW `vector` size is say `vector<4x8xf32>`).
175 Some notable `vector` sizes of interest include:
177 1.  CPU: `vector<HW_vector_size * k>`, `vector<core_count * k’ x
178     HW_vector_size * k>` and `vector<socket_count x core_count * k’ x
179     HW_vector_size * k>`
180 2.  GPU: `vector<warp_size * k>`, `vector<warp_size * k x float4>` and
181     `vector<warp_size * k x 4 x 4 x 4>` for tensor_core sizes,
182 3.  Other accelerators: n-D `vector` as first-class citizens in the HW.
184 Depending on the target, ops on sizes that are not multiples of the HW `vector`
185 size may either produce slow code (e.g. by going through `LLVM` legalization) or
186 may not legalize at all (e.g. some unsupported accelerator X combination of ops
187 and types).
189 ### Transformations Problems Avoided
191 A `vector<16x32x64xf32>` virtual `vector` is a coarse-grained type that can be
192 “unrolled” to HW-specific sizes. The multi-dimensional unrolling factors are
193 carried in the IR by the `vector` type. After unrolling, traditional
194 instruction-level scheduling can be run.
196 The following key transformations (along with the supporting analyses and
197 structural constraints) are completely avoided by operating on a `vector`
198 `ssa-value` abstraction:
200 1.  Loop unroll and unroll-and-jam.
201 2.  Loop and load-store restructuring for register reuse.
202 3.  Load to store forwarding and Mem2reg.
203 4.  Coarsening (raising) from finer-grained `vector` form.
205 Note that “unrolling” in the context of `vector`s corresponds to partial loop
206 unroll-and-jam and not full unrolling. As a consequence this is expected to
207 compose with SW pipelining where applicable and does not result in ICache blow
210 ### The Big Out-Of-Scope Piece: Automatic Vectorization
212 One important piece not discussed here is automatic vectorization (automatically
213 raising from scalar to n-D `vector` ops and types). The TL;DR is that when the
214 first "super-vectorization" prototype was implemented, MLIR was nowhere near as
215 mature as it is today. As we continue building more abstractions in `VV -> HWV`,
216 there is an opportunity to revisit vectorization in MLIR.
218 Since this topic touches on codegen abstractions, it is technically out of the
219 scope of this survey document but there is a lot to discuss in light of
220 structured op type representations and how a vectorization transformation can be
221 reused across dialects. In particular, MLIR allows the definition of dialects at
222 arbitrary levels of granularity and lends itself favorably to progressive
223 lowering. The argument can be made that automatic vectorization on a loops + ops
224 abstraction is akin to raising structural information that has been lost.
225 Instead, it is possible to revisit vectorization as simple pattern rewrites,
226 provided the IR is in a suitable form. For instance, vectorizing a
227 `linalg.generic` op whose semantics match a `matmul` can be done
228 [quite easily with a pattern](https://github.com/tensorflow/mlir/commit/bff722d6b59ab99b998f0c2b9fccd0267d9f93b5).
229 In fact this pattern is trivial to generalize to any type of contraction when
230 targeting the `vector.contract` op, as well as to any field (`+/*`, `min/+`,
231 `max/+`, `or/and`, `logsumexp/+` ...) . In other words, by operating on a higher
232 level of generic abstractions than affine loops, non-trivial transformations
233 become significantly simpler and composable at a finer granularity.
235 Irrespective of the existence of an auto-vectorizer, one can build a notional
236 vector language based on the VectorOps dialect and build end-to-end models with
237 expressing `vector`s in the IR directly and simple pattern-rewrites.
238 [EDSC](https://github.com/llvm/llvm-project/blob/main/mlir/docs/EDSC.md)s
239 provide a simple way of driving such a notional language directly in C++.
241 ## Bikeshed Naming Discussion
243 There are arguments against naming an n-D level of abstraction `vector` because
244 most people associate it with 1-D `vector`s. On the other hand, `vector`s are
245 first-class n-D values in MLIR. The alternative name Tile has been proposed,
246 which conveys higher-D meaning. But it also is one of the most overloaded terms
247 in compilers and hardware. For now, we generally use the `n-D` `vector` name and
248 are open to better suggestions.
250 ## 0D Vectors
252 Vectors of dimension 0 (or _0-D vectors_ or _0D vectors_) are allowed inside
253 MLIR. For instance, a `f32` vector containing one scalar can be denoted as
254 `vector<f32>`. This is similar to the `tensor<f32>` type that is available in
255 TensorFlow or the `memref<f32>` type that is available in MLIR.
257 Generally, a 0D `vector` can be interpreted as a scalar. The benefit of 0D
258 `vector`s, `tensor`s, and `memref`s is that they make it easier to lower code
259 from various frontends such as TensorFlow and make it easier to handle corner
260 cases such as unrolling a loop from 1D to 0D.
262 ## LLVM Lowering Tradeoffs
264 This section describes the tradeoffs involved in lowering the MLIR n-D vector
265 type and operations on it to LLVM-IR. Putting aside the
266 [LLVM Matrix](http://lists.llvm.org/pipermail/llvm-dev/2018-October/126871.html)
267 proposal for now, this assumes LLVM only has built-in support for 1-D vector.
268 The relationship with the LLVM Matrix proposal is discussed at the end of this
269 document.
271 MLIR does not currently support dynamic vector sizes (i.e. SVE style) so the
272 discussion is limited to static rank and static vector sizes (e.g.
273 `vector<4x8x16x32xf32>`). This section discusses operations on vectors in LLVM
274 and MLIR.
276 LLVM instructions are prefixed by the `llvm.` dialect prefix (e.g.
277 `llvm.insertvalue`). Such ops operate exclusively on 1-D vectors and aggregates
278 following the [LLVM LangRef](https://llvm.org/docs/LangRef.html). MLIR
279 operations are prefixed by the `vector.` dialect prefix (e.g.
280 `vector.insertelement`). Such ops operate exclusively on MLIR `n-D` `vector`
281 types.
283 ### Alternatives For Lowering an n-D Vector Type to LLVM
285 Consider a vector of rank n with static sizes `{s_0, ... s_{n-1}}` (i.e. an MLIR
286 `vector<s_0x...s_{n-1}xf32>`). Lowering such an `n-D` MLIR vector type to an
287 LLVM descriptor can be done by either:
289 1.  Flattening to a `1-D` vector: `!llvm<"(s_0*...*s_{n-1})xfloat">` in the MLIR
290     LLVM dialect.
291 2.  Nested aggregate type of `1-D` vector:
292     `!llvm."[s_0x[s_1x[...<s_{n-1}xf32>]]]">` in the MLIR LLVM dialect.
293 3.  A mix of both.
295 There are multiple tradeoffs involved in choosing one or the other that we
296 discuss. It is important to note that “a mix of both” immediately reduces to
297 “nested aggregate type of 1-D vector” with a `vector.cast %0:
298 vector<4x8x16x32xf32> to vector<4x4096xf32>` operation, that flattens the most
299 "k" minor dimensions.
301 ### Constraints Inherited from LLVM (see LangRef)
303 The first constraint was already mentioned: LLVM only supports `1-D` `vector`
304 types natively. Additional constraints are related to the difference in LLVM
305 between vector and aggregate types: `“Aggregate Types are a subset of derived
306 types that can contain multiple member types. Arrays and structs are aggregate
307 types. Vectors are not considered to be aggregate types.”.`
309 This distinction is also reflected in some of the operations. For `1-D` vectors,
310 the operations `llvm.extractelement`, `llvm.insertelement`, and
311 `llvm.shufflevector` apply, with direct support for dynamic indices. For `n-D`
312 vectors with `n>1`, and thus aggregate types at LLVM level, the more restrictive
313 operations `llvm.extractvalue` and `llvm.insertvalue` apply, which only accept
314 static indices. There is no direct shuffling support for aggregate types.
316 The next sentence illustrates a recurrent tradeoff, also found in MLIR, between
317 “value types” (subject to SSA use-def chains) and “memory types” (subject to
318 aliasing and side-effects): `“Structures in memory are accessed using ‘load’ and
319 ‘store’ by getting a pointer to a field with the llvm.getelementptr instruction.
320 Structures in registers are accessed using the llvm.extractvalue and
321 llvm.insertvalue instructions.”`
323 When transposing this to MLIR, `llvm.getelementptr` works on pointers to `n-D`
324 vectors in memory. For `n-D`, vectors values that live in registers we can use
325 `vector.extract` and `vector.insert` which do not accept dynamic indices. Note
326 that this is consistent with hardware considerations as discussed below.
328 An alternative is to use an LLVM `1-D` `vector` type for which one can use
329 `llvm.extractelement`, `llvm.insertelement` and `llvm.shufflevector`. These
330 operations accept dynamic indices. The implication is that one has to use a
331 flattened lowering of an MLIR n-D vector to an LLVM 1-D vector.
333 There are multiple tradeoffs involved that mix implications on the programming
334 model, execution on actual HW and what is visible or hidden from codegen. They
335 are discussed in the following sections.
337 ### Nested Aggregate
339 Pros:
341 1.  Natural encoding n-D vector -> (n-1)-D aggregate over 1-D vector.
342 2.  No need for linearization / delinearization logic inserted everywhere.
343 3.  `llvm.insertvalue`, `llvm.extractvalue` of `(n-k)-D` aggregate is natural.
344 4.  `llvm.insertelement`, `llvm.extractelement`, `llvm.shufflevector` over `1-D`
345     vector type is natural.
347 Cons:
349 1.  `llvm.insertvalue` / `llvm.extractvalue` does not accept dynamic indices but
350     only static ones.
351 2.  Dynamic indexing on the non-most-minor dimension requires roundtrips to
352     memory.
353 3.  Special intrinsics and native instructions in LLVM operate on `1-D` vectors.
354     This is not expected to be a practical limitation thanks to a `vector.cast
355     %0: vector<4x8x16x32xf32> to vector<4x4096xf32>` operation, that flattens
356     the most minor dimensions (see the bigger picture in implications on
357     codegen).
359 ### Flattened 1-D Vector Type
361 Pros:
363 1.  `insertelement` / `extractelement` / `shufflevector` with dynamic indexing
364     is possible over the whole lowered `n-D` vector type.
365 2.  Supports special intrinsics and native operations.
367 Cons:
369 1.  Requires linearization/delinearization logic everywhere, translations are
370     complex.
371 2.  Hides away the real HW structure behind dynamic indexing: at the end of the
372     day, HW vector sizes are generally fixed and multiple vectors will be needed
373     to hold a vector that is larger than the HW.
374 3.  Unlikely peephole optimizations will result in good code: arbitrary dynamic
375     accesses, especially at HW vector boundaries unlikely to result in regular
376     patterns.
378 ### Discussion
380 #### HW Vectors and Implications on the SW and the Programming Model
382 As of today, the LLVM model only support `1-D` vector types. This is
383 unsurprising because historically, the vast majority of HW only supports `1-D`
384 vector registers. We note that multiple HW vendors are in the process of
385 evolving to higher-dimensional physical vectors.
387 In the following discussion, let's assume the HW vector size is `1-D` and the SW
388 vector size is `n-D`, with `n >= 1`. The same discussion would apply with `2-D`
389 HW `vector` size and `n >= 2`. In this context, most HW exhibit a vector
390 register file. The number of such vectors is fixed. Depending on the rank and
391 sizes of the SW vector abstraction and the HW vector sizes and number of
392 registers, an `n-D` SW vector type may be materialized by a mix of multiple
393 `1-D` HW vector registers + memory locations at a given point in time.
395 The implication of the physical HW constraints on the programming model are that
396 one cannot index dynamically across hardware registers: a register file can
397 generally not be indexed dynamically. This is because the register number is
398 fixed and one either needs to unroll explicitly to obtain fixed register numbers
399 or go through memory. This is a constraint familiar to CUDA programmers: when
400 declaring a `private float a[4]`; and subsequently indexing with a *dynamic*
401 value results in so-called **local memory** usage (i.e. roundtripping to
402 memory).
404 #### Implication on codegen
406 MLIR `n-D` vector types are currently represented as `(n-1)-D` arrays of `1-D`
407 vectors when lowered to LLVM. This introduces the consequences on static vs
408 dynamic indexing discussed previously: `extractelement`, `insertelement` and
409 `shufflevector` on `n-D` vectors in MLIR only support static indices. Dynamic
410 indices are only supported on the most minor `1-D` vector but not the outer
411 `(n-1)-D`. For other cases, explicit load / stores are required.
413 The implications on codegen are as follows:
415 1.  Loops around `vector` values are indirect addressing of vector values, they
416     must operate on explicit load / store operations over `n-D` vector types.
417 2.  Once an `n-D` `vector` type is loaded into an SSA value (that may or may not
418     live in `n` registers, with or without spilling, when eventually lowered),
419     it may be unrolled to smaller `k-D` `vector` types and operations that
420     correspond to the HW. This level of MLIR codegen is related to register
421     allocation and spilling that occur much later in the LLVM pipeline.
422 3.  HW may support >1-D vectors with intrinsics for indirect addressing within
423     these vectors. These can be targeted thanks to explicit `vector_cast`
424     operations from MLIR `k-D` vector types and operations to LLVM `1-D`
425     vectors + intrinsics.
427 Alternatively, we argue that directly lowering to a linearized abstraction hides
428 away the codegen complexities related to memory accesses by giving a false
429 impression of magical dynamic indexing across registers. Instead we prefer to
430 make those very explicit in MLIR and allow codegen to explore tradeoffs.
431 Different HW will require different tradeoffs in the sizes involved in steps 1.,
432 2. and 3.
434 Decisions made at the MLIR level will have implications at a much later stage in
435 LLVM (after register allocation). We do not envision to expose concerns related
436 to modeling of register allocation and spilling to MLIR explicitly. Instead,
437 each target will expose a set of "good" target operations and `n-D` vector
438 types, associated with costs that `PatterRewriters` at the MLIR level will be
439 able to target. Such costs at the MLIR level will be abstract and used for
440 ranking, not for accurate performance modeling. In the future such costs will be
441 learned.
443 #### Implication on Lowering to Accelerators
445 To target accelerators that support higher dimensional vectors natively, we can
446 start from either `1-D` or `n-D` vectors in MLIR and use `vector.cast` to
447 flatten the most minor dimensions to `1-D` `vector<Kxf32>` where `K` is an
448 appropriate constant. Then, the existing lowering to LLVM-IR immediately
449 applies, with extensions for accelerator-specific intrinsics.
451 It is the role of an Accelerator-specific vector dialect (see codegen flow in
452 the figure above) to lower the `vector.cast`. Accelerator -> LLVM lowering would
453 then consist of a bunch of `Accelerator -> Accelerator` rewrites to perform the
454 casts composed with `Accelerator -> LLVM` conversions + intrinsics that operate
455 on `1-D` `vector<Kxf32>`.
457 Some of those rewrites may need extra handling, especially if a reduction is
458 involved. For example, `vector.cast %0: vector<K1x...xKnxf32> to vector<Kxf32>`
459 when `K != K1 * … * Kn` and some arbitrary irregular `vector.cast %0:
460 vector<4x4x17xf32> to vector<Kxf32>` may introduce masking and intra-vector
461 shuffling that may not be worthwhile or even feasible, i.e. infinite cost.
463 However `vector.cast %0: vector<K1x...xKnxf32> to vector<Kxf32>` when `K = K1 *
464 … * Kn` should be close to a noop.
466 As we start building accelerator-specific abstractions, we hope to achieve
467 retargetable codegen: the same infra is used for CPU, GPU and accelerators with
468 extra MLIR patterns and costs.
470 #### Implication on calling external functions that operate on vectors
472 It is possible (likely) that we additionally need to linearize when calling an
473 external function.
475 ### Relationship to LLVM matrix type proposal.
477 The LLVM matrix proposal was formulated 1 year ago but seemed to be somewhat
478 stalled until recently. In its current form, it is limited to 2-D matrix types
479 and operations are implemented with LLVM intrinsics. In contrast, MLIR sits at a
480 higher level of abstraction and allows the lowering of generic operations on
481 generic n-D vector types from MLIR to aggregates of 1-D LLVM vectors. In the
482 future, it could make sense to lower to the LLVM matrix abstraction also for CPU
483 even though MLIR will continue needing higher level abstractions.
485 On the other hand, one should note that as MLIR is moving to LLVM, this document
486 could become the unifying abstraction that people should target for 1-D vectors
487 and the LLVM matrix proposal can be viewed as a subset of this work.
489 ### Conclusion
491 The flattened 1-D vector design in the LLVM matrix proposal is good in a
492 HW-specific world with special intrinsics. This is a good abstraction for
493 register allocation, Instruction-Level-Parallelism and
494 SoftWare-Pipelining/Modulo Scheduling optimizations at the register level.
495 However MLIR codegen operates at a higher level of abstraction where we want to
496 target operations on coarser-grained vectors than the HW size and on which
497 unroll-and-jam is applied and patterns across multiple HW vectors can be
498 matched.
500 This makes “nested aggregate type of 1-D vector” an appealing abstraction for
501 lowering from MLIR because:
503 1.  it does not hide complexity related to the buffer vs value semantics and the
504     memory subsystem and
505 2.  it does not rely on LLVM to magically make all the things work from a too
506     low-level abstraction.
508 The use of special intrinsics in a `1-D` LLVM world is still available thanks to
509 an explicit `vector.cast` op.
511 ## Operations
513 [include "Dialects/VectorOps.md"]