Skip to content

Commit 7b273b0

Browse files
committed
[mlir][vector][docs] Document indexed vs. non-indexed arguments
Adds a section to the Vector dialect documentation introducing the distinction between **indexed** and **non-indexed** arguments for "read"/"write"-like operations. The goal is to provide guidance on improving terminology consistency within the Vector dialect and to establish a point of reference for future discussions. Credits to Diego Caballero <[email protected]> for proposing this terminology in on of the PRs.
1 parent 5ce4045 commit 7b273b0

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

mlir/docs/Dialects/Vector.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,96 @@ expressing `vector`s in the IR directly and simple pattern-rewrites.
257257
[EDSC](https://github.com/llvm/llvm-project/blob/main/mlir/docs/EDSC.md)s
258258
provide a simple way of driving such a notional language directly in C++.
259259

260+
### Taxonomy for "Read"/"Write" Operations
261+
262+
Below is a list of vector dialect operations that move values from an abstract
263+
**source** to an abstract **destination**, i.e. "read"/"write" operations:
264+
265+
* `vector.load`, `vector.store`, `vector.transfer_read`,
266+
`vector.transfer_write`, `vector.gather`, `vector.scatter`,
267+
`vector.compressstore`, `vector.expandload`, `vector.maskedload`,
268+
`vector.maskedstore`, `vector.extract`, `vector.insert`,
269+
`vector.scalable_extract`, `vector.scalable_insert`,
270+
`vector.extract_strided_slice`, `vector.insert_strided_slice`.
271+
272+
#### Current Naming in Vector Dialect
273+
| **Vector Dialect Op** | **Operand Names** | **Operand Types** | **Result Name** | **Result Type** |
274+
|--------------------------------|--------------------------|-------------------------------|------------------|----------------------|
275+
| `vector.load` | `base` | `memref` | `result` | `vector` |
276+
| `vector.store` | `valueToStore`, `base` | `vector`, `memref` | - | - |
277+
| `vector.transfer_read` | `source` | `memref` / `tensor` | `vector` | `vector` |
278+
| `vector.transfer_write` | `vector`, `source` | `vector`, `memref`/ `tensor` | `result` | `vector` |
279+
| `vector.gather` | `base` | `memref` | `result` | `vector` |
280+
| `vector.scatter` | `valueToStore`, `base` | `vector`, `memref` | - | - |
281+
| `vector.expandload` | `base` | `memref` | `result` | `vector` |
282+
| `vector.compressstore` | `valueToStore`,`base` | `vector`, `memref` | - | - |
283+
| `vector.maskedload` | `base` | `memref` | `result` | `vector` |
284+
| `vector.maskedstore` | `valueToStore`, `base` | `vector`, `memref` | - | - |
285+
| `vector.extract` | `vector` | `vector` | `result` | `scalar` / `vector` |
286+
| `vector.insert` | `source`, `dest` | `scalar` / `vector`, `vector` | `result` | `vector` |
287+
| `vector.scalable_extract` | `source` | `vector` | `res` | `scalar` / `vector` |
288+
| `vector.scalable_insert` | `source`, `dest` | `scalar` / `vector`, `vector` | `res` | `vector` |
289+
| `vector.extract_strided_slice` | `vector` | `vector` | (missing name) | `vector` |
290+
| `vector.insert_strided_slice` | `source`, `dest` | `vector` | `res` | `vector` |
291+
292+
Note that "read" operations take one operand ("from"), whereas "write"
293+
operations require two ("value-to-store" and "to").
294+
295+
### Observations
296+
Each "read" operation has a "from" argument, while each "write" operation has a
297+
"to" and a "value-to-store" operand. However, the naming conventions are
298+
**inconsistent**, making it difficult to extract common patterns or determine
299+
operand roles. Here are some inconsistencies:
300+
301+
- `getBase()` in `vector.load` refers to the **"from"** operand (source).
302+
- `getBase()` in `vector.store` refers to the **"to"** operand (destination).
303+
- `vector.transfer_read` and `vector.transfer_write` use `getSource()`, which:
304+
- **Conflicts** with the `vector.load` / `vector.store` naming pattern.
305+
- **Does not clearly indicate** whether the operand represents a **source**
306+
or **destination**.
307+
- `vector.insert` defines `getSource()` and `getDest()`, making the distinction
308+
between "to" and "from" operands **clear**. However, its sibling operation,
309+
`vector.extract`, only defines `getVector()`, making it unclear whether it
310+
represents a **source** or **destination**.
311+
- `vector.store` uses `getValueToStore()`, whereas
312+
`vector.insert_strided_slice` does not.
313+
314+
There is **no consistent way** to identify:
315+
- `"from"` (read operand)
316+
- `"to"` (write operand)
317+
- `"value-to-store"` (written value)
318+
319+
### Indexed vs. Non-Indexed Taxonomy
320+
A more consistent way to classify "to", "from", and "value-to-store" arguments
321+
is by determining whether an operand is _indexed_ or _non-indexed_.
322+
323+
#### **Example: `vector.transfer_read` and `vector.transfer_write`**
324+
```mlir
325+
Indexed Operand
326+
|
327+
%res = vector.transfer_read %A[%expr1, %expr2, %expr3, %expr4]
328+
{ permutation_map : (d0,d1,d2,d3) -> (d2,0,d0) } :
329+
memref<?x?x?x?xf32>, vector<3x4x5xf32>
330+
\
331+
Non-Indexed Result
332+
333+
Non-Indexed Operand Indexed Operand
334+
\ /
335+
vector.transfer_write %4, %arg1[%c3, %c3]
336+
{permutation_map = (d0, d1)->(d0, d1)}
337+
: vector<1x1x4x3xf32>, memref<?x?xvector<4x3xf32>>
338+
```
339+
340+
Using the "indexed" vs. "non-indexed" classification, we can systematically
341+
differentiate between "to", "from" and "value-to-store" arguments across
342+
operations:
343+
* "to" is always _indexed_ for "write" operations, "value-to-store" is
344+
_non-indexed_,
345+
* "from" is always _indexed_ for "read" operations.
346+
347+
In addition, for "read" operations, we can view the "result" as a non-indexed
348+
argument.
349+
260350
## Bikeshed Naming Discussion
261351

262352
There are arguments against naming an n-D level of abstraction `vector` because

0 commit comments

Comments
 (0)