[llvm-branch-commits] [mlir] [MLIR][OpenMP] Clause-based OpenMP operation definition (PR #92523)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri May 17 04:04:23 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Sergio Afonso (skatrak)

<details>
<summary>Changes</summary>

This patch updates `OpenMP_Op` definitions to be based on the new set of `OpenMP_Clause` definitions, and to take advantage of clause-based automatically-generated argument lists, descriptions, assembly format and class declarations.

There are also changes introduced to the clause operands structures to match the current set of tablegen clause definitions. These two are very closely linked and should be kept in sync. It would probably be a good idea to try generating clause operands structures from the tablegen `OpenMP_Clause` definitions in the future.

As a result of this change, arguments for some operations have been reordered. This patch also addresses this by updating affected operation build calls and unit tests. Some other updates to tests related to the order of arguments in the resulting assembly format and others due to certain previous inconsistencies in the printing/parsing of clauses are addressed.

The printer and parser functions for the `map` clause are updated, so that they are able to handle `map` clauses linked to entry block arguments as well as those which aren't.

---

Patch is 106.52 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/92523.diff


11 Files Affected:

- (modified) mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h (+26-10) 
- (modified) mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td (+298-846) 
- (modified) mlir/lib/Conversion/SCFToOpenMP/SCFToOpenMP.cpp (+2-1) 
- (modified) mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp (+50-28) 
- (modified) mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp (+1-1) 
- (modified) mlir/test/Dialect/OpenMP/invalid.mlir (+10-10) 
- (modified) mlir/test/Dialect/OpenMP/ops.mlir (+25-26) 
- (modified) mlir/test/Target/LLVMIR/omptarget-llvm.mlir (+3-3) 
- (modified) mlir/test/Target/LLVMIR/omptarget-nowait-llvm.mlir (+1-1) 
- (modified) mlir/test/Target/LLVMIR/omptarget-parallel-llvm.mlir (+2-2) 
- (modified) mlir/test/Target/LLVMIR/openmp-llvm.mlir (+1-1) 


``````````diff
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h
index 244cee1dd635b..bd0d44f932981 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h
@@ -39,6 +39,10 @@ struct AllocateClauseOps {
   llvm::SmallVector<Value> allocatorVars, allocateVars;
 };
 
+struct CancelDirectiveNameClauseOps {
+  ClauseCancellationConstructTypeAttr cancelDirectiveNameAttr;
+};
+
 struct CollapseClauseOps {
   llvm::SmallVector<Value> loopLBVar, loopUBVar, loopStepVar;
 };
@@ -48,6 +52,10 @@ struct CopyprivateClauseOps {
   llvm::SmallVector<Attribute> copyprivateFuncs;
 };
 
+struct CriticalNameClauseOps {
+  StringAttr criticalNameAttr;
+};
+
 struct DependClauseOps {
   llvm::SmallVector<Attribute> dependTypeAttrs;
   llvm::SmallVector<Value> dependVars;
@@ -84,6 +92,7 @@ struct GrainsizeClauseOps {
 struct HasDeviceAddrClauseOps {
   llvm::SmallVector<Value> hasDeviceAddrVars;
 };
+
 struct HintClauseOps {
   IntegerAttr hintAttr;
 };
@@ -117,10 +126,6 @@ struct MergeableClauseOps {
   UnitAttr mergeableAttr;
 };
 
-struct NameClauseOps {
-  StringAttr nameAttr;
-};
-
 struct NogroupClauseOps {
   UnitAttr nogroupAttr;
 };
@@ -209,8 +214,12 @@ struct UntiedClauseOps {
   UnitAttr untiedAttr;
 };
 
-struct UseDeviceClauseOps {
-  llvm::SmallVector<Value> useDevicePtrVars, useDeviceAddrVars;
+struct UseDeviceAddrClauseOps {
+  llvm::SmallVector<Value> useDeviceAddrVars;
+};
+
+struct UseDevicePtrClauseOps {
+  llvm::SmallVector<Value> useDevicePtrVars;
 };
 
 //===----------------------------------------------------------------------===//
@@ -225,7 +234,13 @@ template <typename... Mixins>
 struct Clauses : public Mixins... {};
 } // namespace detail
 
-using CriticalClauseOps = detail::Clauses<HintClauseOps, NameClauseOps>;
+using CancelClauseOps =
+    detail::Clauses<CancelDirectiveNameClauseOps, IfClauseOps>;
+
+using CancellationPointClauseOps =
+    detail::Clauses<CancelDirectiveNameClauseOps>;
+
+using CriticalClauseOps = detail::Clauses<CriticalNameClauseOps, HintClauseOps>;
 
 // TODO `indirect` clause.
 using DeclareTargetClauseOps = detail::Clauses<DeviceTypeClauseOps>;
@@ -264,10 +279,11 @@ using TargetClauseOps =
     detail::Clauses<AllocateClauseOps, DependClauseOps, DeviceClauseOps,
                     HasDeviceAddrClauseOps, IfClauseOps, InReductionClauseOps,
                     IsDevicePtrClauseOps, MapClauseOps, NowaitClauseOps,
-                    PrivateClauseOps, ReductionClauseOps, ThreadLimitClauseOps>;
+                    PrivateClauseOps, ThreadLimitClauseOps>;
 
-using TargetDataClauseOps = detail::Clauses<DeviceClauseOps, IfClauseOps,
-                                            MapClauseOps, UseDeviceClauseOps>;
+using TargetDataClauseOps =
+    detail::Clauses<DeviceClauseOps, IfClauseOps, MapClauseOps,
+                    UseDeviceAddrClauseOps, UseDevicePtrClauseOps>;
 
 using TargetEnterExitUpdateDataClauseOps =
     detail::Clauses<DependClauseOps, DeviceClauseOps, IfClauseOps, MapClauseOps,
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 0a688635e69da..49eedfeba2ee0 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -16,7 +16,7 @@
 
 include "mlir/Dialect/LLVMIR/LLVMOpBase.td"
 include "mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td"
-include "mlir/Dialect/OpenMP/OpenMPAttrDefs.td"
+include "mlir/Dialect/OpenMP/OpenMPClauses.td"
 include "mlir/Dialect/OpenMP/OpenMPOpBase.td"
 include "mlir/Interfaces/ControlFlowInterfaces.td"
 include "mlir/Interfaces/SideEffectInterfaces.td"
@@ -126,26 +126,25 @@ def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove]> {
 //===----------------------------------------------------------------------===//
 
 def ParallelOp : OpenMP_Op<"parallel", [
-                 AutomaticAllocationScope, AttrSizedOperandSegments,
-                 DeclareOpInterfaceMethods<LoopWrapperInterface>,
-                 DeclareOpInterfaceMethods<OutlineableOpenMPOpInterface>,
-                 RecursiveMemoryEffects, ReductionClauseInterface]> {
+    AttrSizedOperandSegments, AutomaticAllocationScope,
+    DeclareOpInterfaceMethods<LoopWrapperInterface>,
+    DeclareOpInterfaceMethods<OutlineableOpenMPOpInterface>,
+    RecursiveMemoryEffects
+  ], [
+    // TODO: Sort clauses alphabetically.
+    OpenMP_IfClause, OpenMP_NumThreadsClause, OpenMP_AllocateClause,
+    OpenMP_ReductionClause, OpenMP_ProcBindClause, OpenMP_PrivateClause
+  ], singleRegion = true> {
   let summary = "parallel construct";
   let description = [{
     The parallel construct includes a region of code which is to be executed
     by a team of threads.
 
-    The optional $if_expr_var parameter specifies a boolean result of a
+    The optional `if_expr` parameter specifies a boolean result of a
     conditional check. If this value is 1 or is not provided then the parallel
     region runs as normal, if it is 0 then the parallel region is executed with
     one thread.
 
-    The optional $num_threads_var parameter specifies the number of threads which
-    should be used to execute the parallel region.
-
-    The $allocators_vars and $allocate_vars parameters are a variadic list of values
-    that specify the memory allocator to be used to obtain storage for private values.
-
     Reductions can be performed in a parallel construct by specifying reduction
     accumulator variables in `reduction_vars` and symbols referring to reduction
     declarations in the `reductions` attribute. Each reduction is identified
@@ -158,37 +157,21 @@ def ParallelOp : OpenMP_Op<"parallel", [
     into the final value, which is available in the accumulator after all the
     threads complete.
 
-    The optional $proc_bind_val attribute controls the thread affinity for the execution
-    of the parallel region.
-
-    The optional byref attribute controls whether reduction arguments are passed by
-    reference or by value.
-  }];
-
-  let arguments = (ins Optional<I1>:$if_expr_var,
-             Optional<IntLikeType>:$num_threads_var,
-             Variadic<AnyType>:$allocate_vars,
-             Variadic<AnyType>:$allocators_vars,
-             Variadic<OpenMP_PointerLikeType>:$reduction_vars,
-             OptionalAttr<SymbolRefArrayAttr>:$reductions,
-             OptionalAttr<ProcBindKindAttr>:$proc_bind_val,
-             Variadic<AnyType>:$private_vars,
-             OptionalAttr<SymbolRefArrayAttr>:$privatizers,
-             UnitAttr:$byref);
-
-  let regions = (region AnyRegion:$region);
+    The optional `byref` attribute controls whether reduction arguments are
+    passed by reference or by value.
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>,
     OpBuilder<(ins CArg<"const ParallelClauseOps &">:$clauses)>
   ];
-  let extraClassDeclaration = [{
-    /// Returns the number of reduction variables.
-    unsigned getNumReductionVars() { return getReductionVars().size(); }
-  }];
+
+  // TODO: Use default assembly format inherited from OpenMP_Op after printing
+  // and parsing of the parallel region is not intermingled with printing and
+  // parsing of reduction and private clauses.
   let assemblyFormat = [{
     oilist(
-          `if` `(` $if_expr_var `:` type($if_expr_var) `)`
+          `if` `(` $if_expr `)`
           | `num_threads` `(` $num_threads_var `:` type($num_threads_var) `)`
           | `allocate` `(`
               custom<AllocateAndAllocator>(
@@ -201,6 +184,7 @@ def ParallelOp : OpenMP_Op<"parallel", [
                              $reductions, $private_vars, type($private_vars),
                              $privatizers) attr-dict
   }];
+
   let hasVerifier = 1;
 }
 
@@ -220,63 +204,27 @@ def TerminatorOp : OpenMP_Op<"terminator", [Terminator, Pure]> {
 // 2.7 teams Construct
 //===----------------------------------------------------------------------===//
 def TeamsOp : OpenMP_Op<"teams", [
-              AttrSizedOperandSegments, RecursiveMemoryEffects,
-              ReductionClauseInterface]> {
+    AttrSizedOperandSegments, RecursiveMemoryEffects
+  ], [
+    // TODO: Complete clause list (private).
+    // TODO: Sort clauses alphabetically.
+    OpenMP_NumTeamsClause, OpenMP_IfClause, OpenMP_ThreadLimitClause,
+    OpenMP_AllocateClause, OpenMP_ReductionClause
+  ], singleRegion = true> {
   let summary = "teams construct";
   let description = [{
     The teams construct defines a region of code that triggers the creation of a
     league of teams. Once created, the number of teams remains constant for the
     duration of its code region.
 
-    The optional $num_teams_upper and $num_teams_lower specify the limit on the
-    number of teams to be created. If only the upper bound is specified, it acts
-    as if the lower bound was set to the same value. It is not supported to set
-    $num_teams_lower if $num_teams_upper is not specified. They define a closed
-    range, where both the lower and upper bounds are included.
-
-    If the $if_expr is present and it evaluates to `false`, the number of teams
+    If the `if_expr` is present and it evaluates to `false`, the number of teams
     created is one.
-
-    The optional $thread_limit specifies the limit on the number of threads.
-
-    The $allocators_vars and $allocate_vars parameters are a variadic list of
-    values that specify the memory allocator to be used to obtain storage for
-    private values.
-  }];
-
-  let arguments = (ins Optional<AnyInteger>:$num_teams_lower,
-                       Optional<AnyInteger>:$num_teams_upper,
-                       Optional<I1>:$if_expr,
-                       Optional<AnyInteger>:$thread_limit,
-                       Variadic<AnyType>:$allocate_vars,
-                       Variadic<AnyType>:$allocators_vars,
-                       Variadic<OpenMP_PointerLikeType>:$reduction_vars,
-                       OptionalAttr<SymbolRefArrayAttr>:$reductions);
-
-  let regions = (region AnyRegion:$region);
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const TeamsClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist(
-      `num_teams` `(` ( $num_teams_lower^ `:` type($num_teams_lower) )? `to`
-                        $num_teams_upper `:` type($num_teams_upper) `)`
-    | `if` `(` $if_expr `)`
-    | `thread_limit` `(` $thread_limit `:` type($thread_limit) `)`
-    | `reduction` `(`
-        custom<ReductionVarList>(
-          $reduction_vars, type($reduction_vars), $reductions
-        ) `)`
-    | `allocate` `(`
-        custom<AllocateAndAllocator>(
-          $allocate_vars, type($allocate_vars),
-          $allocators_vars, type($allocators_vars)
-        ) `)`
-    ) $region attr-dict
-  }];
-
   let hasVerifier = 1;
 }
 
@@ -284,19 +232,22 @@ def TeamsOp : OpenMP_Op<"teams", [
 // 2.8.1 Sections Construct
 //===----------------------------------------------------------------------===//
 
-def SectionOp : OpenMP_Op<"section", [HasParent<"SectionsOp">]> {
+def SectionOp : OpenMP_Op<"section", [HasParent<"SectionsOp">],
+                          singleRegion = true> {
   let summary = "section directive";
   let description = [{
     A section operation encloses a region which represents one section in a
     sections construct. A section op should always be surrounded by an
     `omp.sections` operation.
   }];
-  let regions = (region AnyRegion:$region);
   let assemblyFormat = "$region attr-dict";
 }
 
-def SectionsOp : OpenMP_Op<"sections", [AttrSizedOperandSegments,
-                           ReductionClauseInterface]> {
+def SectionsOp : OpenMP_Op<"sections", [AttrSizedOperandSegments], [
+    // TODO: Complete clause list (private).
+    // TODO: Sort clauses alphabetically.
+    OpenMP_ReductionClause, OpenMP_AllocateClause, OpenMP_NowaitClause
+  ], singleRegion = true> {
   let summary = "sections construct";
   let description = [{
     The sections construct is a non-iterative worksharing construct that
@@ -317,38 +268,17 @@ def SectionsOp : OpenMP_Op<"sections", [AttrSizedOperandSegments,
     into the final value, which is available in the accumulator after all the
     sections complete.
 
-    The $allocators_vars and $allocate_vars parameters are a variadic list of values
-    that specify the memory allocator to be used to obtain storage for private values.
-
-    The `nowait` attribute, when present, signifies that there should be no
-    implicit barrier at the end of the construct.
-  }];
-  let arguments = (ins Variadic<OpenMP_PointerLikeType>:$reduction_vars,
-                       OptionalAttr<SymbolRefArrayAttr>:$reductions,
-                       Variadic<AnyType>:$allocate_vars,
-                       Variadic<AnyType>:$allocators_vars,
-                       UnitAttr:$nowait);
+    The optional `byref` attribute controls whether reduction arguments are
+    passed by reference or by value.
+  }] # clausesDescription;
 
+  // Override region definition.
   let regions = (region SizedRegion<1>:$region);
 
   let builders = [
     OpBuilder<(ins CArg<"const SectionsClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist( `reduction` `(`
-              custom<ReductionVarList>(
-                $reduction_vars, type($reduction_vars), $reductions
-              ) `)`
-          | `allocate` `(`
-              custom<AllocateAndAllocator>(
-                $allocate_vars, type($allocate_vars),
-                $allocators_vars, type($allocators_vars)
-              ) `)`
-          | `nowait` $nowait
-    ) $region attr-dict
-  }];
-
   let hasVerifier = 1;
   let hasRegionVerifier = 1;
 }
@@ -357,45 +287,23 @@ def SectionsOp : OpenMP_Op<"sections", [AttrSizedOperandSegments,
 // 2.8.2 Single Construct
 //===----------------------------------------------------------------------===//
 
-def SingleOp : OpenMP_Op<"single", [AttrSizedOperandSegments]> {
+def SingleOp : OpenMP_Op<"single", [AttrSizedOperandSegments], [
+    // TODO: Complete clause list (private).
+    OpenMP_AllocateClause, OpenMP_CopyprivateClause, OpenMP_NowaitClause
+  ], singleRegion = true> {
   let summary = "single directive";
   let description = [{
     The single construct specifies that the associated structured block is
     executed by only one of the threads in the team (not necessarily the
     master thread), in the context of its implicit task. The other threads
     in the team, which do not execute the block, wait at an implicit barrier
-    at the end of the single construct unless a nowait clause is specified.
-
-    If copyprivate variables and functions are specified, then each thread
-    variable is updated with the variable value of the thread that executed
-    the single region, using the specified copy functions.
-  }];
-
-  let arguments = (ins Variadic<AnyType>:$allocate_vars,
-                       Variadic<AnyType>:$allocators_vars,
-                       Variadic<OpenMP_PointerLikeType>:$copyprivate_vars,
-                       OptionalAttr<SymbolRefArrayAttr>:$copyprivate_funcs,
-                       UnitAttr:$nowait);
-
-  let regions = (region AnyRegion:$region);
+    at the end of the single construct.
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const SingleClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist(`allocate` `(`
-              custom<AllocateAndAllocator>(
-                $allocate_vars, type($allocate_vars),
-                $allocators_vars, type($allocators_vars)
-              ) `)`
-          |`nowait` $nowait
-          |`copyprivate` `(`
-              custom<CopyPrivateVarList>(
-                $copyprivate_vars, type($copyprivate_vars), $copyprivate_funcs
-              ) `)`
-    ) $region attr-dict
-  }];
   let hasVerifier = 1;
 }
 
@@ -403,9 +311,11 @@ def SingleOp : OpenMP_Op<"single", [AttrSizedOperandSegments]> {
 // Loop Nest
 //===----------------------------------------------------------------------===//
 
-def LoopNestOp : OpenMP_Op<"loop_nest", [SameVariadicOperandSize,
-                        AllTypesMatch<["lowerBound", "upperBound", "step"]>,
-                        RecursiveMemoryEffects]> {
+def LoopNestOp : OpenMP_Op<"loop_nest", [
+    RecursiveMemoryEffects, SameVariadicOperandSize
+  ], [
+    OpenMP_CollapseClause
+  ], singleRegion = true> {
   let summary = "rectangular loop nest";
   let description = [{
     This operation represents a collapsed rectangular loop nest. For each
@@ -442,29 +352,24 @@ def LoopNestOp : OpenMP_Op<"loop_nest", [SameVariadicOperandSize,
     non-perfectly nested loops.
   }];
 
-  let arguments = (ins Variadic<IntLikeType>:$lowerBound,
-                       Variadic<IntLikeType>:$upperBound,
-                       Variadic<IntLikeType>:$step,
-                       UnitAttr:$inclusive);
+  let arguments = !con(clausesArgs, (ins UnitAttr:$inclusive));
 
   let builders = [
     OpBuilder<(ins CArg<"const LoopNestClauseOps &">:$clauses)>
   ];
 
-  let regions = (region AnyRegion:$region);
-
   let extraClassDeclaration = [{
-    /// Returns the number of loops in the loop nest.
-    unsigned getNumLoops() { return getLowerBound().size(); }
-
     /// Returns the induction variables of the loop nest.
     ArrayRef<BlockArgument> getIVs() { return getRegion().getArguments(); }
 
     /// Fills a list of wrapper operations around this loop nest. Wrappers
     /// in the resulting vector will be sorted from innermost to outermost.
     void gatherWrappers(SmallVectorImpl<LoopWrapperInterface> &wrappers);
-  }];
+  }] # clausesExtraClassDeclaration;
 
+  // Disable inherited clause-based declarative assembly format and instead
+  // enable using the custom parser-printer implemented in C++.
+  let assemblyFormat = ?;
   let hasCustomAssemblyFormat = 1;
   let hasVerifier = 1;
 }
@@ -473,10 +378,15 @@ def LoopNestOp : OpenMP_Op<"loop_nest", [SameVariadicOperandSize,
 // 2.9.2 Workshare Loop Construct
 //===----------------------------------------------------------------------===//
 
-def WsloopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments,
-                         DeclareOpInterfaceMethods<LoopWrapperInterface>,
-                         RecursiveMemoryEffects, ReductionClauseInterface,
-                         SingleBlockImplicitTerminator<"TerminatorOp">]> {
+def WsloopOp : OpenMP_Op<"wsloop", [
+    AttrSizedOperandSegments, DeclareOpInterfaceMethods<LoopWrapperInterface>,
+    RecursiveMemoryEffects, SingleBlockImplicitTerminator<"TerminatorOp">
+  ], [
+    // TODO: Complete clause list (allocate, private).
+    // TODO: Sort clauses alphabetically.
+    OpenMP_LinearClause, OpenMP_ReductionClause, OpenMP_ScheduleClause,
+    OpenMP_NowaitClause, OpenMP_OrderedClause, OpenMP_OrderClause
+  ], singleRegion = true> {
   let summary = "worksharing-loop construct";
   let description = [{
     The worksharing-loop construct specifies that the iterations of the loop(s)
@@ -501,11 +411,6 @@ def WsloopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments,
     }
     ```
 
-    The `linear_step_vars` operand additionally specifies the step for each
-    associated linear operand. Note that the `linear_vars` and
-    `linear_step_vars` variadic lists should contain the same number of
-    elements.
-
     Reductions can be performed in a worksharing-loop by specifying reduction
     accumulator variables in `reduction_vars` and symbols referring to reduction
     declarations in the `reductions` attribute. Each reduction is identified
@@ -516,55 +421,18 @@ def WsloopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments,
     iteration into the final value, which is available in the accumulator after
     the loop completes.
 
-    The optional `schedule_val` attribute specifies the loop schedule for this
-    loop, determining how the loop is distributed across the parallel threads.
-    The optional `schedule_chunk_var` associated with this determines further
-    controls this distribution.
-
-    Collapsed loops are represented by the worksharing-loop having a list of
-    indices, bounds and steps where the size of the list is equal to the
-    collapse value.
-
-    The `nowait` attribute, when present, signifies that there should be no
-    implicit barrier at the e...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/92523


More information about the llvm-branch-commits mailing list