Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions mlir/docs/Rationale/RationaleLinalgDialect.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,72 @@ potential by introducing lower-level IR ops and *smaller* Linalg ops.
This gradually reduces the potential, all the way to Loops + VectorOps
and LLVMIR.

### Interchangeability of Forms<a name="forms"></a>

#### The Linalg Forms

The core Linalg operation set has four forms:
* **Generic:** Represented by `linalg.generic` and can encode all perfectly-nested
loop operations.
* **Category:** For example, `linalg.contract` and `linalg.elementwise`, that encode
higher-level semantics of a `linalg.generic` while still representing multiple _named_
operations via attributes and syntax. In the future, other category operations are
planned (e.g.: `linalg.convolution` and `linalg.pooling`).
* **Named:** For example, `linalg.matmul`, `linalg.add`, etc. All _named_ forms that
can be converted to either a single _category_ or _generic_ forms, ie. are _perfectly nested_.
* **Composite:** For example `linalg.softmax` and the `winograd` variations. These
operations are not perfectly nested, and are converted to a list of other operations
(of various dialects).

The forms correlate in the following manner:
```
+ generic
\__ + category
\__ + named
+ composite
```

The `category` and `named` forms are derived from `linalg.generic` and are *equivalent*.
It should always be possible to convert a `named` operation into a `category` and that
into a `generic` and back to `named`. However, it may not be possible to convert a
`generic` into a `named` if there is no such `named` form.

`Composite` operations cannot be converted to the other three classes and forms a
sub-set on its own. But they can use other Linalg forms when expanding. There can be
a pattern-matching transform to detect a graph of operations and convert into a
`composite` operation.

The various forms in the Linalg dialect are meant to facilitate
pattern matching (single operations or DAGs) and to be able to consider
different forms as *canonical* for different transforms.

Linalg's various forms also carry information, and that
information should be preserved as much as possible during the progressive
lowering. A `matmul` operation is a special case of a `contract` operation,
which in turn is a special case of `generic` operation. Transformations on
the more special forms should not be converted to the more generic ones
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this about the ops being converted or the transformations?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both?

The idea is that "ops being converted" help simplify "transformations". So additional conversions between transformations can help if they assume different canonical forms. But IF and only IF the forms are semantically equivalent, thus, linalg forms.

unnecessarily, in the same way that they should not be broken down into
loops + arithmetic if they can still be represented as a Linalg op.

#### Canonical Forms<a name="canonical_forms"></a>

With multiple (often exchangeable) forms, and with transformation simplicity
in mind, compilers should aim for reducing matching and replacing complexity
as much as possible. When matching a single operation with a complex pattern,
having all the information in a `generic` is useful to iteratively match
different patterns in turn. However, when assembling a DAG of operations to
Comment on lines +560 to +562
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, would I ever want to (or need to) match linalg.generic if matching a named (or a category) Op was an option? This implies that in some cases matching linalg.generic might be more desirable, but I've always struggle to find a good example.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you get a named op from the front-end, life is simple. But if you get a generic, you have to make choices.

You can:

  1. run a pass to convert to a named op (currently O(num_gen_attr x num_named_ops)) and then match against that.
  2. iteratively match each generic attribute to a precise form (O(num_gen_attr) only) and match directly.

The first approach is helpful if you have a lot of different forms and want to normalize to a canonical form that your passes expect. One-off cost at the beginning of the pipeline, no cost later on.

The second approach is much faster if only a single pass needs to look at that operation (or multiple passes, one for each operation in different forms), since you match one attribute at a time with a particular pattern (not a list of all patterns).

Makes sense?

form a pattern, it's much simpler to match against named operations (like
`max` + `div` + `reduce` + `broadcast`) than their generic counterparts.

This is where the interchangeability of forms comes in handy. Linalg has the
ability to specialize and generalize in order to convert the IR to a form that
is easier for a particular type of transform. With forms being semantically
equivalent, one can convert back-and-forth throughout the various transforms
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an actual conversion or matching on interface?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actual conversion. We have discussed interfaces last year (the forum links in this PR), but the agreement was to keep using forms for now and see if we can generalize to interfaces later. Having the forms may be a bit too verbose, but it will give us enough data on how to design the interfaces if we end up doing it.

to match the needs of each transform. For that particular transform, such
form can be considered _canonical_ and therefore "expected" for the pattern
to _match_. This reduces complexity of pattern matchers and simplifies compiler
pipelines.

### Composable and Declarative Transformations<a name="declarative_transformations"></a>
Complex and impactful transformations need not be hard to manipulate, write or
maintain. Mixing XLA-style high-level op semantics knowledge with generic
Expand Down
Loading