Skip to content

Commit 551134c

Browse files
authored
[MLIR][OpenMP][Docs] Document clause-based op representation (NFC) (#107234)
This patch documents the clause-based op represetation discussed in [this RFC](https://discourse.llvm.org/t/rfc-clause-based-representation-of-openmp-dialect-operations/79053).
1 parent 8b82fc6 commit 551134c

File tree

1 file changed

+196
-0
lines changed

1 file changed

+196
-0
lines changed

mlir/docs/Dialects/OpenMPDialect/_index.md

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,199 @@ mapping information is named `MapInfoOp` / `omp.map.info`. The same rules are
4141
followed if multiple operations are created for different variants of the same
4242
directive, e.g. `atomic` becomes `Atomic{Read,Write,Update,Capture}Op` /
4343
`omp.atomic.{read,write,update,capture}`.
44+
45+
## Clause-Based Operation Definition
46+
47+
One main feature of the OpenMP specification is that, even though the set of
48+
clauses that could be applied to a given directive is independent from other
49+
directives, these clauses can generally apply to multiple directives. Since
50+
clauses usually define which arguments the corresponding MLIR operation takes,
51+
it is possible (and preferred) to define OpenMP dialect operations based on the
52+
list of clauses taken by the corresponding directive. This makes it simpler to
53+
keep their representation consistent across operations and minimizes redundancy
54+
in the dialect.
55+
56+
To achieve this, the base `OpenMP_Clause` tablegen class has been created. It is
57+
intended to be used to create clause definitions that can be then attached to
58+
multiple `OpenMP_Op` definitions, resulting in the latter inheriting by default
59+
all properties defined by clauses attached, similarly to the trait mechanism.
60+
This mechanism is implemented in
61+
[OpenMPOpBase.td](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/OpenMP/OpenMPOpBase.td).
62+
63+
### Adding a Clause
64+
65+
OpenMP clause definitions are located in
66+
[OpenMPClauses.td](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td).
67+
For each clause, an `OpenMP_Clause` subclass and a definition based on it must
68+
be created. The subclass must take a `bit` template argument for each of the
69+
properties it can populate on associated `OpenMP_Op`s. These must be forwarded
70+
to the base class. The definition must be an instantiation of the base class
71+
where all these template arguments are set to `false`. The definition's name
72+
must be `OpenMP_<Name>Clause`, whereas its base class' must be
73+
`OpenMP_<Name>ClauseSkip`. Following this pattern makes it possible to
74+
optionally skip the inheritance of some properties when defining operations:
75+
[more info](#overriding-clause-inherited-properties).
76+
77+
Clauses can define the following properties:
78+
- `list<Traits> traits`: To be used when having a certain clause always
79+
implies some op trait, like the `map` clause and the `MapClauseOwningInterface`.
80+
- `dag(ins) arguments`: Mandatory property holding values and attributes
81+
used to represent the clause. Argument names use snake_case and should contain
82+
the clause name to avoid name clashes between clauses. Variadic arguments
83+
(non-attributes) must contain the "_vars" suffix.
84+
- `string {req,opt}AssemblyFormat`: Optional formatting strings to produce
85+
custom human-friendly printers and parsers for arguments associated with the
86+
clause. It will be combined with assembly formats for other clauses as explained
87+
[below](#adding-an-operation).
88+
- `string description`: Optional description text to describe the clause and
89+
its representation.
90+
- `string extraClassDeclaration`: Optional C++ declarations to be added to
91+
operation classes including the clause.
92+
93+
For example:
94+
95+
```tablegen
96+
class OpenMP_ExampleClauseSkip<
97+
bit traits = false, bit arguments = false, bit assemblyFormat = false,
98+
bit description = false, bit extraClassDeclaration = false
99+
> : OpenMP_Clause<traits, arguments, assemblyFormat, description,
100+
extraClassDeclaration> {
101+
let arguments = (ins
102+
Optional<AnyType>:$example_var
103+
);
104+
105+
let optAssemblyFormat = [{
106+
`example` `(` $example_var `:` type($example_var) `)`
107+
}];
108+
109+
let description = [{
110+
The `example_var` argument defines the variable to which the EXAMPLE clause
111+
applies.
112+
}];
113+
}
114+
115+
def OpenMP_ExampleClause : OpenMP_ExampleClauseSkip<>;
116+
```
117+
118+
### Adding an Operation
119+
120+
Operations in the OpenMP dialect, located in
121+
[OpenMPOps.td](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td),
122+
can be defined like any other regular operation by just specifying a `mnemonic`
123+
and optional list of `traits` when inheriting from `OpenMP_Op`, and then
124+
defining the expected `description`, `arguments`, etc. properties inside of its
125+
body. However, in most cases, basing the operation definition on its list of
126+
accepted clauses is significantly simpler because some of the properties can
127+
just be inherited from these clauses.
128+
129+
In general, the way to achieve this is to specify, in addition to the `mnemonic`
130+
and optional list of `traits`, a list of `clauses` where all the applicable
131+
`OpenMP_<Name>Clause` definitions are added. Then, the only properties that
132+
would have to be defined in the operation's body are the `summary` and
133+
`description`. For the latter, only the operation itself would have to be
134+
defined, and the description for its clause-inherited arguments is appended
135+
through the inherited `clausesDescription` property.
136+
137+
If the operation is intended to have a single region, this is better achieved by
138+
setting the `singleRegion=true` template argument of `OpenMP_Op` rather manually
139+
populating the `regions` property of the operation, because that way the default
140+
`assemblyFormat` is also updated correspondingly.
141+
142+
For example:
143+
144+
```tablegen
145+
def ExampleOp : OpenMP_Op<"example", traits = [
146+
AttrSizedOperandSegments, ...
147+
], clauses = [
148+
OpenMP_AlignedClause, OpenMP_IfClause, OpenMP_LinearClause, ...
149+
], singleRegion = true> {
150+
let summary = "example construct";
151+
let description = [{
152+
The example construct represents...
153+
}] # clausesDescription;
154+
}
155+
```
156+
157+
This is possible because the `arguments`, `assemblyFormat` and
158+
`extraClassDeclaration` properties of the operation are by default
159+
populated by concatenating the corresponding properties of the clauses on the
160+
list. In the case of the `assemblyFormat`, this involves combining the
161+
`reqAssemblyFormat` and the `optAssemblyFormat` properties. The
162+
`reqAssemblyFormat` of all clauses is concatenated first and separated using
163+
spaces, whereas the `optAssemblyFormat` is wrapped in an `oilist()` and
164+
interleaved with "|" instead of spaces. The resulting `assemblyFormat` contains
165+
the required assembly format strings, followed by the optional assembly format
166+
strings, optionally the `$region` and the `attr-dict`.
167+
168+
### Overriding Clause-Inherited Properties
169+
170+
Although the clause-based definition of operations can greatly reduce work, it's
171+
also somewhat restrictive, since there may be some situations where only part of
172+
the operation definition can be automated in that manner. For a fine-grained
173+
control over properties inherited from each clause two features are available:
174+
175+
- Inhibition of properties. By using `OpenMP_<Name>ClauseSkip` tablegen
176+
classes, the list of properties copied from the clause to the operation can be
177+
selected. For example, `OpenMP_IfClauseSkip<assemblyFormat = true>` would result
178+
in every property defined for the `OpenMP_IfClause` except for the
179+
`assemblyFormat` being used to initially populate the properties of the
180+
operation.
181+
- Augmentation of properties. There are times when there is a need to add to
182+
a clause-populated operation property. Instead of overriding the property in the
183+
definition of the operation and having to manually replicate what would
184+
otherwise be automatically populated before adding to it, some internal
185+
properties are defined to hold this default value: `clausesArgs`,
186+
`clausesAssemblyFormat`, `clauses{Req,Opt}AssemblyFormat` and
187+
`clausesExtraClassDeclaration`.
188+
189+
In the following example, assuming both the `OpenMP_InReductionClause` and the
190+
`OpenMP_ReductionClause` define a `getReductionVars` extra class declaration,
191+
we skip the conflicting `extraClassDeclaration`s inherited by both clauses and
192+
provide another implementation, without having to also re-define other
193+
declarations inherited from the `OpenMP_AllocateClause`:
194+
195+
```tablegen
196+
def ExampleOp : OpenMP_Op<"example", traits = [
197+
AttrSizedOperandSegments, ...
198+
], clauses = [
199+
OpenMP_AllocateClause,
200+
OpenMP_InReductionClauseSkip<extraClassDeclaration = true>,
201+
OpenMP_ReductionClauseSkip<extraClassDeclaration = true>
202+
], singleRegion = true> {
203+
let summary = "example construct";
204+
let description = [{
205+
This operation represents...
206+
}] # clausesDescription;
207+
208+
// Override the clause-populated extraClassDeclaration and add the default
209+
// back via appending clausesExtraClassDeclaration to it. This has the effect
210+
// of adding one declaration. Since this property is skipped for the
211+
// InReduction and Reduction clauses, clausesExtraClassDeclaration won't
212+
// incorporate the definition of this property for these clauses.
213+
let extraClassDeclaration = [{
214+
SmallVector<Value> getReductionVars() {
215+
// Concatenate inReductionVars and reductionVars and return the result...
216+
}
217+
}] # clausesExtraClassDeclaration;
218+
}
219+
```
220+
221+
These features are intended for complex edge cases, but an effort should be made
222+
to avoid having to use them, since they may introduce inconsistencies and
223+
complexity to the dialect.
224+
225+
### Tablegen Verification Pass
226+
227+
As a result of the implicit way in which fundamental properties of MLIR
228+
operations are populated following this approach, and the ability to override
229+
them, forgetting to append clause-inherited values might result in hard to debug
230+
tablegen errors.
231+
232+
For this reason, the `-verify-openmp-ops` tablegen pseudo-backend was created.
233+
It runs before any other tablegen backends are triggered for the
234+
[OpenMPOps.td](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td)
235+
file and warns any time a property defined for a clause is not found in the
236+
corresponding operation, except if it is explicitly skipped as described
237+
[above](#overriding-clause-inherited-properties). This way, in case of a later
238+
tablegen failure while processing OpenMP dialect operations, earlier messages
239+
triggered by that pass can point to a likely solution.

0 commit comments

Comments
 (0)