Skip to content

Commit fec244f

Browse files
committed
[MLIR][OpenMP] Support clause-based representation of operations
Currently, OpenMP operations are defined independently of each other. However, one property of the OpenMP specification is that many clauses can be applied to multiple constructs. Keeping the MLIR representation of clauses consistent across all operations that can accept them is important, but since this information is scattered into multiple operation definitions, it is currently prone to divergence as new features and changes are added to the dialect. Furthermore, centralizing this information allows for a single source of truth and avoids redundancy in the dialect. The proposal in this patch is to make OpenMP clauses independent top level definitions which can then be passed in a template argument list to OpenMP operation definitions, just as it's done for traits. Clauses can define these properties, which are joined together in order to make a default initialization for the fields of the same name of the OpenMP operation: - `traits`: Optional. It gets added to the list of traits of the operation. - `arguments`: Mandatory. It defines how the clause is represented. - `assemblyFormat`: Optional (though it should almost always be defined). This is the declarative definition of the printer/parser for the `arguments`. How these are combined depends on whether this is an optional or required clause. - `description`: Optional. It's used to populate a `clausesDescription` field, so each operation definition must still define a `description` itself. That field is intended to be appended to the end of the `OpenMP_Op`'s `description`. - `extraClassDeclaration`: Optional. It can define some C++ code to be added to every OpenMP operation that includes that clause. In order to give operation definitions fine-grained control over features of a certain clause might need to be inhibited, the `OpenMP_Clause` class takes "skipTraits", "skipArguments", "skipAssemblyFormat", "skipDescription" and "skipExtraClassDeclaration" bit template arguments. These are intended to be used very sparingly for cases where some of the clauses might collide in some way otherwise.
1 parent 9dd5543 commit fec244f

File tree

1 file changed

+163
-2
lines changed

1 file changed

+163
-2
lines changed

mlir/include/mlir/Dialect/OpenMP/OpenMPOpBase.td

Lines changed: 163 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,168 @@ def OpenMP_MapBoundsType : OpenMP_Type<"MapBounds", "map_bounds_ty"> {
4242
// Base classes for OpenMP dialect operations.
4343
//===----------------------------------------------------------------------===//
4444

45-
class OpenMP_Op<string mnemonic, list<Trait> traits = []> :
46-
Op<OpenMP_Dialect, mnemonic, traits>;
45+
// Base class for representing OpenMP clauses.
46+
//
47+
// Clauses are meant to be used in a mixin-style pattern to help define OpenMP
48+
// operations in a scalable way, since often the same clause can be applied to
49+
// multiple different operations.
50+
//
51+
// To keep the representation of clauses consistent across different operations,
52+
// each clause must define a set of arguments (values and attributes) which will
53+
// become input arguments of each OpenMP operation that accepts that clause.
54+
//
55+
// It is also recommended that an assembly format and description are defined
56+
// for each clause wherever posible, to make sure they are always printed,
57+
// parsed and described in the same way.
58+
//
59+
// Optionally, operation traits and extra class declarations might be attached
60+
// to clauses, which will be forwarded to all operations that include them.
61+
//
62+
// Each clause must specify whether it's required or optional. This impacts how
63+
// the `assemblyFormat` for operations including it get generated.
64+
//
65+
// An `OpenMP_Op` can inhibit the inheritance of `traits`, `arguments`,
66+
// `assemblyFormat`, `description` and `extraClassDeclaration` fields from any
67+
// given `OpenMP_Clause` by setting to 1 the corresponding "skip" template
68+
// argument bit.
69+
class OpenMP_Clause<bit isRequired, bit skipTraits, bit skipArguments,
70+
bit skipAssemblyFormat, bit skipDescription,
71+
bit skipExtraClassDeclaration> {
72+
bit required = isRequired;
73+
74+
bit ignoreTraits = skipTraits;
75+
list<Trait> traits = [];
76+
77+
bit ignoreArgs = skipArguments;
78+
dag arguments;
79+
80+
bit ignoreAsmFormat = skipAssemblyFormat;
81+
string assemblyFormat = "";
82+
83+
bit ignoreDesc = skipDescription;
84+
string description = "";
85+
86+
bit ignoreExtraDecl = skipExtraClassDeclaration;
87+
string extraClassDeclaration = "";
88+
}
89+
90+
// Base class for representing OpenMP operations.
91+
//
92+
// This is a subclass of the builtin `Op` for the OpenMP dialect. By default,
93+
// some of its fields are initialized according to the list of OpenMP clauses
94+
// passed as template argument:
95+
// - `traits`: It is a union of the traits list passed as template argument
96+
// and those inherited from the `traits` field of all clauses.
97+
// - `arguments`: They are a concatenation of clause-inherited arguments. They
98+
// are saved to a `clausesArgs` field to allow overriding the arguments
99+
// field in the definition of the operation and still being able to include
100+
// those inherited from clauses.
101+
// - `assemblyFormat`: It is a concatenation of the `assemblyFormat` of
102+
// all required clauses followed by an `oilist()` containing the
103+
// `assemblyFormat` of all optional clauses. The format string is completed
104+
// with $region (if `singleRegion = true`) followed by attr-dict. This field
105+
// remains uninitialized if no non-empty `assemblyFormat` strings are
106+
// inherited from clauses. The `clausesAssemblyFormat` field holds
107+
// all the format except for "$region attr-dict", so that an operation
108+
// overriding `assemblyFormat` can still benefit from the auto-generated
109+
// format for its clauses.
110+
// - `description`: This is still required to be defined by the operation.
111+
// However, a `clausesDescription` field is provided containing a
112+
// concatenation of descriptions of all clauses, to be appended to the
113+
// operation's `description` field.
114+
// - `extraClassDeclaration`: It contains a concatenation of the
115+
// `extraClassDeclaration` of all clauses. This string is also stored in
116+
// `clausesExtraClassDeclaration`, so that an operation overriding this
117+
// field can append the clause-inherited ones as well.
118+
//
119+
// The `regions` field will contain a single `AnyRegion:$region` element if the
120+
// `singleRegion` bit template argument is set to 1. Otherwise, it will be
121+
// empty.
122+
class OpenMP_Op<string mnemonic, list<Trait> traits = [],
123+
list<OpenMP_Clause> clauses = [], bit singleRegion = false> :
124+
Op<OpenMP_Dialect, mnemonic,
125+
// The resulting operation's traits list will be the concatenation of
126+
// explicit operation traits and all traits attached to the clauses of the
127+
// operation. Repetitions are skipped.
128+
!listconcat(traits,
129+
!listremove(
130+
!foldl([]<Trait>,
131+
!foreach(clause,
132+
!filter(fClause, clauses, !not(fClause.ignoreTraits)),
133+
clause.traits),
134+
acc, traitList, !listconcat(acc, !listremove(traitList, acc))),
135+
traits
136+
)
137+
)> {
138+
// Aggregate `arguments` fields of all clauses into a single dag, to be used
139+
// by operations to populate their `arguments` field.
140+
defvar argsFilteredClauses =
141+
!filter(clause, clauses, !not(clause.ignoreArgs));
142+
143+
dag clausesArgs =
144+
!foldl((ins), !foreach(clause, argsFilteredClauses, clause.arguments),
145+
acc, argList, !con(acc, argList));
146+
147+
// Create assembly format string by concatenating format strings separately
148+
// for required and optional clauses. Then, required clauses format strings
149+
// are joined with spaces in between. Optional clauses format strings are
150+
// wrapped into an unsorted list of optional values and separated by "|"
151+
// characters.
152+
153+
// Required clauses.
154+
defvar reqClauses = !filter(clause, clauses, clause.required);
155+
defvar asmFormatFilteredReqClauses =
156+
!filter(clause, reqClauses, !not(!or(clause.ignoreAsmFormat,
157+
!empty(clause.assemblyFormat))));
158+
159+
defvar asmFormatReqClauseStrings =
160+
!foreach(clause, asmFormatFilteredReqClauses, clause.assemblyFormat);
161+
162+
defvar asmFormatReqClauseBody = !interleave(asmFormatReqClauseStrings, " ");
163+
164+
// Optional clauses.
165+
defvar optClauses = !filter(clause, clauses, !not(clause.required));
166+
defvar asmFormatFilteredOptClauses =
167+
!filter(clause, optClauses, !not(!or(clause.ignoreAsmFormat,
168+
!empty(clause.assemblyFormat))));
169+
170+
defvar asmFormatOptClauseStrings =
171+
!foreach(clause, asmFormatFilteredOptClauses, clause.assemblyFormat);
172+
173+
defvar asmFormatOptClauseBody = !interleave(asmFormatOptClauseStrings, "|");
174+
175+
string clausesAssemblyFormat =
176+
!if(!empty(asmFormatReqClauseStrings), "", asmFormatReqClauseBody # " ") #
177+
!if(!empty(asmFormatOptClauseStrings), "",
178+
"oilist(" # asmFormatOptClauseBody # ")");
179+
180+
// Put together descriptions of all clauses into a single string.
181+
defvar descFilteredClauses =
182+
!filter(clause, clauses, !not(clause.ignoreDesc));
183+
184+
string clausesDescription =
185+
!interleave(!foreach(clause, descFilteredClauses, clause.description), "");
186+
187+
// Aggregate `extraClassDeclaration` of all clauses that define it.
188+
defvar extraDeclFilteredClauses =
189+
!filter(clause, clauses, !not(clause.ignoreExtraDecl));
190+
191+
string clausesExtraClassDeclaration =
192+
!interleave(!foreach(clause, extraDeclFilteredClauses,
193+
clause.extraClassDeclaration), "\n");
194+
195+
// The default arguments, assembly format and extra class declarations for
196+
// OpenMP operations are those defined by their args and clauses.
197+
let arguments = clausesArgs;
198+
let assemblyFormat =
199+
!if(!empty(clausesAssemblyFormat), ?,
200+
clausesAssemblyFormat # !if(singleRegion, " $region", "") #
201+
" attr-dict");
202+
let extraClassDeclaration = clausesExtraClassDeclaration;
203+
204+
// By default, the op will have zero regions. Setting `singleRegion = true`
205+
// will result in a single region named `$region`.
206+
let regions = !if(singleRegion, (region AnyRegion:$region), (region));
207+
}
47208

48209
#endif // OPENMP_OP_BASE

0 commit comments

Comments
 (0)