llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-mlir @llvm/pr-subscribers-mlir-openmp Author: Sergio Afonso (skatrak) <details> <summary>Changes</summary> 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). --- Full diff: https://github.com/llvm/llvm-project/pull/107234.diff 1 Files Affected: - (modified) mlir/docs/Dialects/OpenMPDialect/_index.md (+192) ``````````diff diff --git a/mlir/docs/Dialects/OpenMPDialect/_index.md b/mlir/docs/Dialects/OpenMPDialect/_index.md index 43e82a871db066..8abdc731675fc0 100644 --- a/mlir/docs/Dialects/OpenMPDialect/_index.md +++ b/mlir/docs/Dialects/OpenMPDialect/_index.md @@ -39,3 +39,195 @@ mapping information is named `MapInfoOp` / `omp.map.info`. The same rules are followed if multiple operations are created for different variants of the same directive, e.g. `atomic` becomes `Atomic{Read,Write,Update,Capture}Op` / `omp.atomic.{read,write,update,capture}`. + +## Clause-Based Operation Definition + +One main feature of the OpenMP specification is that, even though the set of +clauses that could be applied to a given directive is independent from other +directives, these clauses can generally apply to multiple directives. Since +clauses usually define which arguments the corresponding MLIR operation takes, +it is possible (and preferred) to define OpenMP dialect operations based on the +list of clauses taken by the corresponding directive. This makes it simpler to +keep their representation consistent across operations and minimizes redundancy +in the dialect. + +To achieve this, the base `OpenMP_Clause` tablegen class has been created. It is +intended to be used to create clause definitions that can be then attached to +multiple `OpenMP_Op` definitions, resulting in the latter inheriting by default +all properties defined by clauses attached, similarly to the trait mechanism. +This mechanism is implemented in +[OpenMPOpBase.td](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/OpenMP/OpenMPOpBase.td). + +### Adding a Clause + +OpenMP clause definitions are located in +[OpenMPClauses.td](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td). +For each clause, an `OpenMP_Clause` subclass and a definition based on it must +be created. The subclass must take a `bit` template argument for each of the +properties it may define, listed below, which is forwarded to the base class. +The definition must be an instantiation of the base class where all these +template arguments are set to `false`. The definition's name must be +`OpenMP_<Name>Clause`, whereas its base class' must be +`OpenMP_<Name>ClauseSkip`. Following this pattern makes it possible to +optionally skip the inheritance of some properties when defining operations: +[more info](#overriding-clause-inherited-properties). + +Clauses can define the following properties: + - `list<Traits> traits`: To be used when having a certain clause always +implies some op trait, like the `map` clause and the `MapClauseOwningInterface`. + - `dag(ins) arguments`: Mandatory property holding values and attributes +used to represent the clause. Argument names use snake_case and should contain +the clause name to avoid name clashes between clauses. Variadic arguments +(non-attributes) must contain the "_vars" suffix. + - `string assemblyFormat`: Optional formatting string to produce custom +human-friendly printers and parsers for arguments associated with the clause. +It will be combined with assembly formats for other clauses depending on the +`isRequired` template argument passed to the parent `OpenMP_Clause` class, as +explained [below](#adding-an-operation). + - `string description`: Optional description text to describe the clause and +its representation. + - `string extraClassDeclaration`: Optional C++ declarations to be added to +operation classes including the clause. + +For example: + +```tablegen +class OpenMP_ExampleClauseSkip< + bit traits = false, bit arguments = false, bit assemblyFormat = false, + bit description = false, bit extraClassDeclaration = false + > : OpenMP_Clause</*isRequired=*/false, traits, arguments, assemblyFormat, + description, extraClassDeclaration> { + let arguments = (ins + Optional<AnyType>:$example_var + ); + + let assemblyFormat = [{ + `example` `(` $example_var `:` type($example_var) `)` + }]; + + let description = [{ + The `example_var` argument defines the variable to which the EXAMPLE clause + applies. + }]; +} + +def OpenMP_ExampleClause : OpenMP_ExampleClauseSkip<>; +``` + +### Adding an Operation + +Operations in the OpenMP dialect, located in +[OpenMPOps.td](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td), +can be defined like any other regular operation by just specifying a `mnemonic` +and optional list of `traits` when inheriting from `OpenMP_Op`, and then +defining the expected `description`, `arguments`, etc. properties inside of its +body. However, in most cases, basing the operation definition on its list of +accepted clauses is significantly simpler because some of the properties can +just be inherited from these clauses. + +In general, the way to achieve this is to specify, in addition to the `mnemonic` +and optional list of `traits`, a list of `clauses` where all the applicable +`OpenMP_<Name>Clause` definitions are added. Then, the only properties that +would have to be defined in the operation's body are the `summary` and +`description`. For the latter, only the operation itself would have to be +defined, and the description for its clause-inherited arguments is appended +through the inherited `clausesDescription` property. + +If the operation is intended to have a single region, this is better achieved by +setting the `singleRegion=true` template argument of `OpenMP_Op` rather manually +populating the `regions` property of the operation, because that way the default +`assemblyFormat` is also updated correspondingly. + +For example: + +```tablegen +def ExampleOp : OpenMP_Op<"example", traits = [ + AttrSizedOperandSegments, ... + ], clauses = [ + OpenMP_AlignedClause, OpenMP_IfClause, OpenMP_LinearClause, ... + ], singleRegion = true> { + let summary = "example construct"; + let description = [{ + The example construct represents... + }] # clausesDescription; +} +``` + +This is possible because the `arguments`, `assemblyFormat` and +`extraClassDeclaration` properties of the operation are by default +populated by concatenating the corresponding properties of the clauses on the +list. In the case of the `assemblyFormat`, this also involves splitting the +format strings for required clauses from the ones for optional clauses. The +latter are wrapped in an `oilist()` and interleaved with "|" instead of spaces. + +### Overriding Clause-Inherited Properties + +Although the clause-based definition of operations can greatly reduce work, it's +also somewhat restrictive, since there may be some situations where only part of +the operation definition can be automated in that manner. For a fine-grained +control over properties inherited from each clause two features are available: + + - Inhibition of properties. By using `OpenMP_<Name>ClauseSkip` tablegen +classes, the list of properties copied from the clause to the operation can be +selected. For example, `OpenMP_IfClauseSkip<assemblyFormat = true>` would result +in every property defined for the `OpenMP_IfClause` except for the +`assemblyFormat` being used to initially populate the properties of the +operation. + - Augmentation of properties. There are times when there is a need to add to +a clause-populated operation property. Instead of overriding the property in the +definition of the operation and having to manually replicate what would +otherwise be automatically populated before adding to it, some internal +properties are defined to hold this default value: `clausesArgs`, +`clausesAssemblyFormat` and `clausesExtraClassDeclaration`. + +In the following example, assuming both the `OpenMP_InReductionClause` and the +`OpenMP_ReductionClause` define a `getReductionVars` extra class declaration, +we skip the conflicting `extraClassDeclaration`s inherited by both clauses and +provide another implementation, without having to also re-define other +declarations inherited from the `OpenMP_AllocateClause`: + +```tablegen +def ExampleOp : OpenMP_Op<"example", traits = [ + AttrSizedOperandSegments, ... + ], clauses = [ + OpenMP_AllocateClause, + OpenMP_InReductionClauseSkip<extraClassDeclaration = true>, + OpenMP_ReductionClauseSkip<extraClassDeclaration = true> + ], singleRegion = true> { + let summary = "example construct"; + let description = [{ + This operation represents... + }] # clausesDescription; + + // Override the clause-populated extraClassDeclaration and add the default + // back via appending clausesExtraClassDeclaration to it. This has the effect + // of adding one declaration. Since this property is skipped for the + // InReduction and Reduction clauses, clausesExtraClassDeclaration won't + // incorporate the definition of this property for these clauses. + let extraClassDeclaration = [{ + SmallVector<Value> getReductionVars() { + // Concatenate inReductionVars and reductionVars and return the result... + } + }] # clausesExtraClassDeclaration; +} +``` + +These features are intended for complex edge cases, but an effort should be made +to avoid having to use them, since they may introduce inconsistencies and +complexity to the dialect. + +### Tablegen Verification Pass + +As a result of the implicit way in which fundamental properties of MLIR +operations are populated following this approach, and the ability to override +them, forgetting to append clause-inherited values might result in hard to debug +tablegen errors. + +For this reason, the `-verify-openmp-ops` tablegen pseudo-backend was created. +It runs before any other tablegen backends are triggered for the +[OpenMPOps.td](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td) +file and warns any time a property defined for a clause is not found in the +corresponding operation, except if it is explicitly skipped as described +[above](#overriding-clause-inherited-properties). This way, in case of a later +tablegen failure while processing OpenMP dialect operations, earlier messages +triggered by that pass can point to a likely solution. `````````` </details> https://github.com/llvm/llvm-project/pull/107234 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits