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

Sergio Afonso via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri May 17 04:03:54 PDT 2024


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

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.

>From 6fb9989f6c6c4565f4c19c89be91a34b3978904c Mon Sep 17 00:00:00 2001
From: Sergio Afonso <safonsof at amd.com>
Date: Fri, 17 May 2024 11:23:23 +0100
Subject: [PATCH] [MLIR][OpenMP] Clause-based OpenMP operation definition

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.
---
 .../Dialect/OpenMP/OpenMPClauseOperands.h     |   36 +-
 mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 1144 +++++------------
 .../Conversion/SCFToOpenMP/SCFToOpenMP.cpp    |    3 +-
 mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp  |   78 +-
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      |    2 +-
 mlir/test/Dialect/OpenMP/invalid.mlir         |   20 +-
 mlir/test/Dialect/OpenMP/ops.mlir             |   51 +-
 mlir/test/Target/LLVMIR/omptarget-llvm.mlir   |    6 +-
 .../Target/LLVMIR/omptarget-nowait-llvm.mlir  |    2 +-
 .../LLVMIR/omptarget-parallel-llvm.mlir       |    4 +-
 mlir/test/Target/LLVMIR/openmp-llvm.mlir      |    2 +-
 11 files changed, 419 insertions(+), 929 deletions(-)

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 end of the loop.
-
-    The optional `ordered_val` attribute specifies how many loops are associated
-    with the worksharing-loop construct. The value of zero refers to the ordered
-    clause specified without parameter.
-
-    The optional `order` attribute specifies which order the iterations of the
-    associate loops are executed in. Currently the only option for this
-    attribute is "concurrent".
-
-    The optional `byref` attribute indicates that reduction arguments should be
-    passed by reference.
-  }];
-
-  let arguments = (ins Variadic<AnyType>:$linear_vars,
-             Variadic<I32>:$linear_step_vars,
-             Variadic<OpenMP_PointerLikeType>:$reduction_vars,
-             OptionalAttr<SymbolRefArrayAttr>:$reductions,
-             OptionalAttr<ScheduleKindAttr>:$schedule_val,
-             Optional<AnyType>:$schedule_chunk_var,
-             OptionalAttr<ScheduleModifierAttr>:$schedule_modifier,
-             UnitAttr:$simd_modifier,
-             UnitAttr:$nowait,
-             UnitAttr:$byref,
-             ConfinedAttr<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$ordered_val,
-             OptionalAttr<OrderKindAttr>:$order_val);
+    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 WsloopClauseOps &">:$clauses)>
   ];
 
-  let regions = (region AnyRegion:$region);
-
-  let extraClassDeclaration = [{
-    /// Returns the number of reduction variables.
-    unsigned getNumReductionVars() { return getReductionVars().size(); }
-  }];
-  let hasCustomAssemblyFormat = 1;
+  // TODO: Use default assembly format inherited from OpenMP_Op after printing
+  // and parsing of the workshare loop region is not intermingled with printing
+  // and parsing of reduction clauses.
   let assemblyFormat = [{
     oilist(`linear` `(`
               custom<LinearClause>($linear_vars, type($linear_vars),
@@ -580,6 +448,7 @@ def WsloopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments,
     ) custom<Wsloop>($region, $reduction_vars, type($reduction_vars),
                      $reductions) attr-dict
   }];
+
   let hasVerifier = 1;
 }
 
@@ -587,11 +456,15 @@ def WsloopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments,
 // Simd construct [2.9.3.1]
 //===----------------------------------------------------------------------===//
 
-def SimdOp : OpenMP_Op<"simd", [AttrSizedOperandSegments,
-                       DeclareOpInterfaceMethods<LoopWrapperInterface>,
-                       RecursiveMemoryEffects,
-                       SingleBlockImplicitTerminator<"TerminatorOp">]> {
- let summary = "simd construct";
+def SimdOp : OpenMP_Op<"simd", [
+    AttrSizedOperandSegments, DeclareOpInterfaceMethods<LoopWrapperInterface>,
+    RecursiveMemoryEffects, SingleBlockImplicitTerminator<"TerminatorOp">
+  ], [
+    // TODO: Complete clause list (linear, private, reduction).
+    OpenMP_AlignedClause, OpenMP_IfClause, OpenMP_NontemporalClause,
+    OpenMP_OrderClause, OpenMP_SafelenClause, OpenMP_SimdlenClause
+  ], singleRegion = true> {
+  let summary = "simd construct";
   let description = [{
     The simd construct can be applied to a loop to indicate that the loop can be
     transformed into a SIMD loop (that is, multiple iterations of the loop can
@@ -601,27 +474,6 @@ def SimdOp : OpenMP_Op<"simd", [AttrSizedOperandSegments,
     operation and a terminator. The operation must be another compatible loop
     wrapper or an `omp.loop_nest`.
 
-    The `alignment_values` attribute additionally specifies alignment of each
-    corresponding aligned operand. Note that `$aligned_vars` and
-    `alignment_values` should contain the same number of elements.
-
-    When an if clause is present and evaluates to false, the preferred number of
-    iterations to be executed concurrently is one, regardless of whether
-    a simdlen clause is specified.
-
-    The optional `nontemporal` attribute specifies variables which have low
-    temporal locality across the iterations where they are accessed.
-
-    The optional `order` attribute specifies which order the iterations of the
-    associate loops are executed in. Currently the only option for this
-    attribute is "concurrent".
-
-    When a simdlen clause is present, the preferred number of iterations to be
-    executed concurrently is the value provided to the simdlen clause.
-
-    The safelen clause specifies that no two concurrent iterations within a
-    SIMD chunk can have a distance in the logical iteration space that is
-    greater than or equal to the value given in the clause.
     ```
     omp.simd <clauses> {
       omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
@@ -634,37 +486,16 @@ def SimdOp : OpenMP_Op<"simd", [AttrSizedOperandSegments,
       omp.terminator
     }
     ```
-  }];
-
-  // TODO: Add other clauses
-  let arguments = (ins Variadic<OpenMP_PointerLikeType>:$aligned_vars,
-             OptionalAttr<I64ArrayAttr>:$alignment_values,
-             Optional<I1>:$if_expr,
-             Variadic<OpenMP_PointerLikeType>:$nontemporal_vars,
-             OptionalAttr<OrderKindAttr>:$order_val,
-             ConfinedAttr<OptionalAttr<I64Attr>, [IntPositive]>:$simdlen,
-             ConfinedAttr<OptionalAttr<I64Attr>, [IntPositive]>:$safelen
-     );
 
-  let regions = (region AnyRegion:$region);
+    When an if clause is present and evaluates to false, the preferred number of
+    iterations to be executed concurrently is one, regardless of whether
+    a simdlen clause is specified.
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const SimdClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist(`aligned` `(`
-              custom<AlignedClause>($aligned_vars, type($aligned_vars),
-                                   $alignment_values) `)`
-          |`if` `(` $if_expr `)`
-          |`nontemporal` `(`  $nontemporal_vars `:` type($nontemporal_vars) `)`
-          |`order` `(` custom<ClauseAttr>($order_val) `)`
-          |`simdlen` `(` $simdlen  `)`
-          |`safelen` `(` $safelen  `)`
-    ) $region attr-dict
-  }];
-
-  let hasCustomAssemblyFormat = 1;
   let hasVerifier = 1;
 }
 
@@ -686,16 +517,20 @@ def YieldOp : OpenMP_Op<"yield",
     OpBuilder<(ins), [{ build($_builder, $_state, {}); }]>
   ];
 
-  let assemblyFormat = [{ ( `(` $results^ `:` type($results) `)` )? attr-dict}];
+  let assemblyFormat = "( `(` $results^ `:` type($results) `)` )? attr-dict";
 }
 
 //===----------------------------------------------------------------------===//
 // Distribute construct [2.9.4.1]
 //===----------------------------------------------------------------------===//
-def DistributeOp : OpenMP_Op<"distribute", [AttrSizedOperandSegments,
-                             DeclareOpInterfaceMethods<LoopWrapperInterface>,
-                             RecursiveMemoryEffects,
-                             SingleBlockImplicitTerminator<"TerminatorOp">]> {
+def DistributeOp : OpenMP_Op<"distribute", [
+    AttrSizedOperandSegments, DeclareOpInterfaceMethods<LoopWrapperInterface>,
+    RecursiveMemoryEffects, SingleBlockImplicitTerminator<"TerminatorOp">
+  ], [
+    // TODO: Complete clause list (private).
+    // TODO: Sort clauses alphabetically.
+    OpenMP_DistScheduleClause, OpenMP_AllocateClause, OpenMP_OrderClause
+  ], singleRegion = true> {
   let summary = "distribute construct";
   let description = [{
     The distribute construct specifies that the iterations of one or more loops
@@ -716,11 +551,6 @@ def DistributeOp : OpenMP_Op<"distribute", [AttrSizedOperandSegments,
     operation and a terminator. The operation must be another compatible loop
     wrapper or an `omp.loop_nest`.
 
-    The `dist_schedule_static` attribute specifies the  schedule for this
-    loop, determining how the loop is distributed across the parallel threads.
-    The optional `schedule_chunk` associated with this determines further
-    controls this distribution.
-
     ```mlir
     omp.distribute <clauses> {
       omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
@@ -733,33 +563,12 @@ def DistributeOp : OpenMP_Op<"distribute", [AttrSizedOperandSegments,
       omp.terminator
     }
     ```
-    // TODO: private_var, firstprivate_var, lastprivate_var, collapse
-  }];
-  let arguments = (ins
-             UnitAttr:$dist_schedule_static,
-             Optional<IntLikeType>:$chunk_size,
-             Variadic<AnyType>:$allocate_vars,
-             Variadic<AnyType>:$allocators_vars,
-             OptionalAttr<OrderKindAttr>:$order_val);
-
-  let regions = (region AnyRegion:$region);
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const DistributeClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist(`dist_schedule_static` $dist_schedule_static
-          |`chunk_size` `(` $chunk_size `:` type($chunk_size) `)`
-          |`order` `(` custom<ClauseAttr>($order_val) `)`
-          |`allocate` `(`
-             custom<AllocateAndAllocator>(
-               $allocate_vars, type($allocate_vars),
-               $allocators_vars, type($allocators_vars)
-             ) `)`
-    ) $region attr-dict
-  }];
-
   let hasVerifier = 1;
 }
 
@@ -767,9 +576,16 @@ def DistributeOp : OpenMP_Op<"distribute", [AttrSizedOperandSegments,
 // 2.10.1 task Construct
 //===----------------------------------------------------------------------===//
 
-def TaskOp : OpenMP_Op<"task", [AttrSizedOperandSegments,
-                       OutlineableOpenMPOpInterface, AutomaticAllocationScope,
-                       ReductionClauseInterface]> {
+def TaskOp : OpenMP_Op<"task", [
+    AttrSizedOperandSegments, AutomaticAllocationScope,
+    OutlineableOpenMPOpInterface
+  ], [
+    // TODO: Complete clause list (affinity, detach, private).
+    // TODO: Sort clauses alphabetically.
+    OpenMP_IfClause, OpenMP_FinalClause, OpenMP_UntiedClause,
+    OpenMP_MergeableClause, OpenMP_InReductionClause,
+    OpenMP_PriorityClause, OpenMP_DependClause, OpenMP_AllocateClause
+  ], singleRegion = true> {
   let summary = "task construct";
   let description = [{
     The task construct defines an explicit task.
@@ -783,92 +599,31 @@ def TaskOp : OpenMP_Op<"task", [AttrSizedOperandSegments,
     execution cannot be resumed until execution of the structured block that is
     associated with the generated task is completed.
 
-    When a `final` clause is present on a task construct and the `final_expr`
-    evaluates to `true`, the generated task will be a "final task". All task
-    constructs encountered during execution of a final task will generate final
-    and included tasks.
-
-    If the `untied` clause is present on a task construct, any thread in the
-    team can resume the task region after a suspension. The `untied` clause is
-    ignored if a `final` clause is present on the same task construct and the
-    `final_expr` evaluates to `true`, or if a task is an included task.
-
-    When the `mergeable` clause is present on a task construct, the generated
-    task is a "mergeable task".
-
     The `in_reduction` clause specifies that this particular task (among all the
     tasks in current taskgroup, if any) participates in a reduction.
+  }] # clausesDescription;
 
-    The `priority` clause is a hint for the priority of the generated task.
-    The `priority` is a non-negative integer expression that provides a hint for
-    task execution order. Among all tasks ready to be executed, higher priority
-    tasks (those with a higher numerical value in the priority clause
-    expression) are recommended to execute before lower priority ones. The
-    default priority-value when no priority clause is specified should be
-    assumed to be zero (the lowest priority).
-
-    The `depends` and `depend_vars` arguments are variadic lists of values
-    that specify the dependencies of this particular task in relation to
-    other tasks.
-
-    The `allocators_vars` and `allocate_vars` arguments are a variadic list of
-    values that specify the memory allocator to be used to obtain storage for
-    private values.
-
-  }];
-
-  // TODO: depend, affinity and detach clauses
-  let arguments = (ins Optional<I1>:$if_expr,
-                       Optional<I1>:$final_expr,
-                       UnitAttr:$untied,
-                       UnitAttr:$mergeable,
-                       Variadic<OpenMP_PointerLikeType>:$in_reduction_vars,
-                       OptionalAttr<SymbolRefArrayAttr>:$in_reductions,
-                       Optional<I32>:$priority,
-                       OptionalAttr<TaskDependArrayAttr>:$depends,
-                       Variadic<OpenMP_PointerLikeType>:$depend_vars,
-                       Variadic<AnyType>:$allocate_vars,
-                       Variadic<AnyType>:$allocators_vars);
-  let regions = (region AnyRegion:$region);
   let builders = [
     OpBuilder<(ins CArg<"const TaskClauseOps &">:$clauses)>
   ];
-  let assemblyFormat = [{
-    oilist(`if` `(` $if_expr `)`
-          |`final` `(` $final_expr `)`
-          |`untied` $untied
-          |`mergeable` $mergeable
-          |`in_reduction` `(`
-              custom<ReductionVarList>(
-                $in_reduction_vars, type($in_reduction_vars), $in_reductions
-              ) `)`
-          |`priority` `(` $priority `)`
-          |`allocate` `(`
-              custom<AllocateAndAllocator>(
-                $allocate_vars, type($allocate_vars),
-                $allocators_vars, type($allocators_vars)
-              ) `)`
-          |`depend` `(`
-              custom<DependVarList>(
-                $depend_vars, type($depend_vars), $depends
-              ) `)`
-    ) $region attr-dict
-  }];
-  let extraClassDeclaration = [{
-    /// Returns the reduction variables
-    SmallVector<Value> getReductionVars() {
-      return SmallVector<Value>(getInReductionVars().begin(),
-                                getInReductionVars().end());
-    }
-  }];
+
   let hasVerifier = 1;
 }
 
-def TaskloopOp : OpenMP_Op<"taskloop", [AttrSizedOperandSegments,
-                           AutomaticAllocationScope,
-                           DeclareOpInterfaceMethods<LoopWrapperInterface>,
-                           RecursiveMemoryEffects, ReductionClauseInterface,
-                           SingleBlockImplicitTerminator<"TerminatorOp">]> {
+def TaskloopOp : OpenMP_Op<"taskloop", [
+    AttrSizedOperandSegments, AutomaticAllocationScope,
+    DeclareOpInterfaceMethods<LoopWrapperInterface>, RecursiveMemoryEffects,
+    SingleBlockImplicitTerminator<"TerminatorOp">
+  ], [
+    // TODO: Complete clause list (private).
+    // TODO: Sort clauses alphabetically.
+    OpenMP_IfClause, OpenMP_FinalClause, OpenMP_UntiedClause,
+    OpenMP_MergeableClause,
+    OpenMP_InReductionClauseSkip<extraClassDeclaration = true>,
+    OpenMP_ReductionClauseSkip<extraClassDeclaration = true>,
+    OpenMP_PriorityClause, OpenMP_AllocateClause, OpenMP_GrainsizeClause,
+    OpenMP_NumTasksClause, OpenMP_NogroupClause
+  ], singleRegion = true> {
   let summary = "taskloop construct";
   let description = [{
     The taskloop construct specifies that the iterations of one or more
@@ -901,18 +656,6 @@ def TaskloopOp : OpenMP_Op<"taskloop", [AttrSizedOperandSegments,
     use of a variable in an `if` clause expression of a taskloop construct
     causes an implicit reference to the variable in all enclosing constructs.
 
-    When a `final` clause is present on a taskloop construct and the `final`
-    clause expression evaluates to `true`, the generated tasks will be final
-    tasks. The use of a variable in a `final` clause expression of a taskloop
-    construct causes an implicit reference to the variable in all enclosing
-    constructs.
-
-    If the `untied` clause is specified, all tasks generated by the taskloop
-    construct are untied tasks.
-
-    When the `mergeable` clause is present on a taskloop construct, each
-    generated task is a mergeable task.
-
     Reductions can be performed in a loop by specifying reduction accumulator
     variables in `reduction_vars` or `in_reduction_vars` and symbols referring
     to reduction declarations in the `reductions` or `in_reductions` attribute.
@@ -925,6 +668,9 @@ def TaskloopOp : OpenMP_Op<"taskloop", [AttrSizedOperandSegments,
     specifies how to combine the values from each iteration into the final
     value, which is available in the accumulator after the loop completes.
 
+    The optional `byref` attribute controls whether reduction arguments are
+    passed by reference or by value.
+
     If an `in_reduction` clause is present on the taskloop construct, the
     behavior is as if each generated task was defined by a task construct on
     which an `in_reduction` clause with the same reduction operator and list
@@ -939,91 +685,28 @@ def TaskloopOp : OpenMP_Op<"taskloop", [AttrSizedOperandSegments,
     reduction operator and list items is present. Thus, the generated tasks are
     participants of the reduction defined by the `task_reduction` clause that
     was applied to the implicit taskgroup construct.
-
-    When a `priority` clause is present on a taskloop construct, the generated
-    tasks use the `priority-value` as if it was specified for each individual
-    task. If the `priority` clause is not specified, tasks generated by the
-    taskloop construct have the default task priority (zero).
-
-    The `allocators_vars` and `allocate_vars` arguments are a variadic list of
-    values that specify the memory allocator to be used to obtain storage for
-    private values.
-
-    If a `grainsize` clause is present on the taskloop construct, the number of
-    logical loop iterations assigned to each generated task is greater than or
-    equal to the minimum of the value of the grain-size expression and the
-    number of logical loop iterations, but less than two times the value of the
-    grain-size expression.
-
-    If `num_tasks` is specified, the taskloop construct creates as many tasks as
-    the minimum of the num-tasks expression and the number of logical loop
-    iterations. Each task must have at least one logical loop iteration.
-
-    By default, the taskloop construct executes as if it was enclosed in a
-    taskgroup construct with no statements or directives outside of the taskloop
-    construct. Thus, the taskloop construct creates an implicit taskgroup
-    region. If the `nogroup` clause is present, no implicit taskgroup region is
-    created.
-  }];
-
-  let arguments = (ins Optional<I1>:$if_expr,
-                       Optional<I1>:$final_expr,
-                       UnitAttr:$untied,
-                       UnitAttr:$mergeable,
-                       Variadic<OpenMP_PointerLikeType>:$in_reduction_vars,
-                       OptionalAttr<SymbolRefArrayAttr>:$in_reductions,
-                       Variadic<OpenMP_PointerLikeType>:$reduction_vars,
-                       OptionalAttr<SymbolRefArrayAttr>:$reductions,
-                       Optional<IntLikeType>:$priority,
-                       Variadic<AnyType>:$allocate_vars,
-                       Variadic<AnyType>:$allocators_vars,
-                       Optional<IntLikeType>: $grain_size,
-                       Optional<IntLikeType>: $num_tasks,
-                       UnitAttr: $nogroup);
-
-  let regions = (region AnyRegion:$region);
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const TaskloopClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist(`if` `(` $if_expr `)`
-          |`final` `(` $final_expr `)`
-          |`untied` $untied
-          |`mergeable` $mergeable
-          |`in_reduction` `(`
-              custom<ReductionVarList>(
-                $in_reduction_vars, type($in_reduction_vars), $in_reductions
-              ) `)`
-          |`reduction` `(`
-              custom<ReductionVarList>(
-                $reduction_vars, type($reduction_vars), $reductions
-              ) `)`
-          |`priority` `(` $priority `:` type($priority) `)`
-          |`allocate` `(`
-              custom<AllocateAndAllocator>(
-                $allocate_vars, type($allocate_vars),
-                $allocators_vars, type($allocators_vars)
-              ) `)`
-          |`grain_size` `(` $grain_size `:` type($grain_size) `)`
-          |`num_tasks` `(` $num_tasks `:` type($num_tasks) `)`
-          |`nogroup` $nogroup
-    ) $region attr-dict
-  }];
-
   let extraClassDeclaration = [{
     /// Returns the reduction variables
     SmallVector<Value> getAllReductionVars();
+
     void getEffects(SmallVectorImpl<MemoryEffects::EffectInstance> &effects);
-  }];
+  }] # clausesExtraClassDeclaration;
 
   let hasVerifier = 1;
 }
 
-def TaskgroupOp : OpenMP_Op<"taskgroup", [AttrSizedOperandSegments,
-                            ReductionClauseInterface,
-                            AutomaticAllocationScope]> {
+def TaskgroupOp : OpenMP_Op<"taskgroup", [
+    AttrSizedOperandSegments, AutomaticAllocationScope
+  ], [
+    // TODO: Sort clauses alphabetically.
+    OpenMP_TaskReductionClause, OpenMP_AllocateClause
+  ], singleRegion = true> {
   let summary = "taskgroup construct";
   let description = [{
     The taskgroup construct specifies a wait on completion of child tasks of the
@@ -1036,49 +719,13 @@ def TaskgroupOp : OpenMP_Op<"taskgroup", [AttrSizedOperandSegments,
     an implicit task scheduling point at the end of the taskgroup region. The
     current task is suspended at the task scheduling point until all tasks in
     the taskgroup set complete execution.
-
-    The `task_reduction` clause specifies a reduction among tasks. For each list
-    item, the number of copies is unspecified. Any copies associated with the
-    reduction are initialized before they are accessed by the tasks
-    participating in the reduction. After the end of the region, the original
-    list item contains the result of the reduction.
-
-    The `allocators_vars` and `allocate_vars` arguments are a variadic list of
-    values that specify the memory allocator to be used to obtain storage for
-    private values.
-  }];
-
-  let arguments = (ins Variadic<OpenMP_PointerLikeType>:$task_reduction_vars,
-                       OptionalAttr<SymbolRefArrayAttr>:$task_reductions,
-                       Variadic<AnyType>:$allocate_vars,
-                       Variadic<AnyType>:$allocators_vars);
-
-  let regions = (region AnyRegion:$region);
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const TaskgroupClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist(`task_reduction` `(`
-              custom<ReductionVarList>(
-                $task_reduction_vars, type($task_reduction_vars), $task_reductions
-              ) `)`
-          |`allocate` `(`
-              custom<AllocateAndAllocator>(
-                $allocate_vars, type($allocate_vars),
-                $allocators_vars, type($allocators_vars)
-              ) `)`
-    ) $region attr-dict
-  }];
-
-  let extraClassDeclaration = [{
-    /// Returns the reduction variables
-    operand_range getAllReductionVars() { return getTaskReductionVars(); }
-  }];
-
   let hasVerifier = 1;
-
 }
 
 //===----------------------------------------------------------------------===//
@@ -1098,18 +745,23 @@ def TaskyieldOp : OpenMP_Op<"taskyield"> {
 //===----------------------------------------------------------------------===//
 // 2.13.7 flush Construct
 //===----------------------------------------------------------------------===//
-def FlushOp : OpenMP_Op<"flush"> {
+def FlushOp : OpenMP_Op<"flush", clauses = [
+    // TODO: Complete clause list (memory_order).
+  ]> {
   let summary = "flush construct";
   let description = [{
     The flush construct executes the OpenMP flush operation. This operation
-    makes a thread’s temporary view of memory consistent with memory and
+    makes a thread's temporary view of memory consistent with memory and
     enforces an order on the memory operations of the variables explicitly
     specified or implied.
-  }];
+  }] # clausesDescription;
+
+  let arguments = !con((ins Variadic<OpenMP_PointerLikeType>:$varList),
+                       clausesArgs);
 
-  let arguments = (ins Variadic<OpenMP_PointerLikeType>:$varList);
+  // Override inherited assembly format to include `varList`.
+  let assemblyFormat = "( `(` $varList^ `:` type($varList) `)` )? attr-dict";
 
-  let assemblyFormat = [{ ( `(` $varList^ `:` type($varList) `)` )? attr-dict}];
   let extraClassDeclaration = [{
     /// The number of variable operands.
     unsigned getNumVariableOperands() {
@@ -1119,7 +771,7 @@ def FlushOp : OpenMP_Op<"flush"> {
     Value getVariableOperand(unsigned i) {
       return getOperand(i);
     }
-  }];
+  }] # clausesExtraClassDeclaration;
 }
 
 //===----------------------------------------------------------------------===//
@@ -1322,8 +974,11 @@ def MapInfoOp : OpenMP_Op<"map.info", [AttrSizedOperandSegments]> {
 // 2.14.2 target data Construct
 //===---------------------------------------------------------------------===//
 
-def TargetDataOp: OpenMP_Op<"target_data", [AttrSizedOperandSegments,
-                                            MapClauseOwningOpInterface]>{
+def TargetDataOp: OpenMP_Op<"target_data", [AttrSizedOperandSegments], [
+    // TODO: Sort clauses alphabetically.
+    OpenMP_IfClause, OpenMP_DeviceClause, OpenMP_UseDevicePtrClause,
+    OpenMP_UseDeviceAddrClause, OpenMP_MapClause
+  ], singleRegion = true> {
   let summary = "target data construct";
   let description = [{
     Map variables to a device data environment for the extent of the region.
@@ -1334,48 +989,16 @@ def TargetDataOp: OpenMP_Op<"target_data", [AttrSizedOperandSegments,
     to and from the offloading device when multiple target regions are using
     the same data.
 
-    The optional $if_expr 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 target
     region runs on a device, if it is 0 then the target region is executed
     on the host device.
-
-    The optional $device parameter specifies the device number for the target
-    region.
-
-    The optional $use_device_ptr specifies the device pointers to the
-    corresponding list items in the device data environment.
-
-    The optional $use_device_addr specifies the address of the objects in the
-    device data enviornment.
-
-    The $map_operands specifies the locator-list operands of the map clause.
-
-    The $map_types specifies the types and modifiers for the map clause.
-
-    TODO:  depend clause and map_type_modifier values iterator and mapper.
-  }];
-
-  let arguments = (ins Optional<I1>:$if_expr,
-                       Optional<AnyInteger>:$device,
-                       Variadic<OpenMP_PointerLikeType>:$use_device_ptr,
-                       Variadic<OpenMP_PointerLikeType>:$use_device_addr,
-                       Variadic<AnyType>:$map_operands);
-
-  let regions = (region AnyRegion:$region);
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const TargetDataClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist(`if` `(` $if_expr `:` type($if_expr) `)`
-    | `device` `(` $device `:` type($device) `)`
-    | `map_entries` `(` $map_operands `:` type($map_operands) `)`
-    | `use_device_ptr` `(` $use_device_ptr `:` type($use_device_ptr) `)`
-    | `use_device_addr` `(` $use_device_addr `:` type($use_device_addr) `)`)
-    $region attr-dict
-  }];
-
   let hasVerifier = 1;
 }
 
@@ -1383,57 +1006,29 @@ def TargetDataOp: OpenMP_Op<"target_data", [AttrSizedOperandSegments,
 // 2.14.3 target enter data Construct
 //===---------------------------------------------------------------------===//
 
-def TargetEnterDataOp: OpenMP_Op<"target_enter_data",
-                                                 [AttrSizedOperandSegments,
-                                                  MapClauseOwningOpInterface]>{
-  let  summary = "target enter data construct";
+def TargetEnterDataOp: OpenMP_Op<"target_enter_data", [
+    AttrSizedOperandSegments
+  ], [
+    // TODO: Sort clauses alphabetically.
+    OpenMP_IfClause, OpenMP_DeviceClause, OpenMP_DependClause,
+    OpenMP_NowaitClause, OpenMP_MapClause
+  ]> {
+  let summary = "target enter data construct";
   let description = [{
     The target enter data directive specifies that variables are mapped to
     a device data environment. The target enter data directive is a
     stand-alone directive.
 
-    The optional $if_expr 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 target
     region runs on a device, if it is 0 then the target region is executed on
     the host device.
-
-    The optional $device parameter specifies the device number for the
-    target region.
-
-    The optional $nowait eliminates the implicit barrier so the parent task
-    can make progress even if the target task is not yet completed.
-
-    The $map_operands specifies the locator-list operands of the map clause.
-
-    The $map_types specifies the types and modifiers for the map clause.
-
-    The `depends` and `depend_vars` arguments are variadic lists of values
-    that specify the dependencies of this particular target task in relation to
-    other tasks.
-
-    TODO:  map_type_modifier values iterator and mapper.
-  }];
-
-  let arguments = (ins Optional<I1>:$if_expr,
-                       Optional<AnyInteger>:$device,
-                       OptionalAttr<TaskDependArrayAttr>:$depends,
-                       Variadic<OpenMP_PointerLikeType>:$depend_vars,
-                       UnitAttr:$nowait,
-                       Variadic<AnyType>:$map_operands);
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const TargetEnterExitUpdateDataClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist(`if` `(` $if_expr `:` type($if_expr) `)`
-    | `device` `(` $device `:` type($device) `)`
-    | `nowait` $nowait
-    | `map_entries` `(` $map_operands `:` type($map_operands) `)`
-    | `depend` `(` custom<DependVarList>($depend_vars, type($depend_vars), $depends) `)`
-    ) attr-dict
-   }];
-
   let hasVerifier = 1;
 }
 
@@ -1441,57 +1036,28 @@ def TargetEnterDataOp: OpenMP_Op<"target_enter_data",
 // 2.14.4 target exit data Construct
 //===---------------------------------------------------------------------===//
 
-def TargetExitDataOp: OpenMP_Op<"target_exit_data",
-                                                 [AttrSizedOperandSegments,
-                                                  MapClauseOwningOpInterface]>{
-  let  summary = "target exit data construct";
+def TargetExitDataOp: OpenMP_Op<"target_exit_data", [AttrSizedOperandSegments],
+  [
+    // TODO: Sort clauses alphabetically.
+    OpenMP_IfClause, OpenMP_DeviceClause, OpenMP_DependClause,
+    OpenMP_NowaitClause, OpenMP_MapClause
+  ]> {
+  let summary = "target exit data construct";
   let description = [{
     The target exit data directive specifies that variables are mapped to a
     device data environment. The target exit data directive is
     a stand-alone directive.
 
-    The optional $if_expr 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 target
     region runs on a device, if it is 0 then the target region is executed
     on the host device.
-
-    The optional $device parameter specifies the device number for the
-    target region.
-
-    The optional $nowait eliminates the implicit barrier so the parent
-    task can make progress even if the target task is not yet completed.
-
-    The $map_operands specifies the locator-list operands of the map clause.
-
-    The $map_types specifies the types and modifiers for the map clause.
-
-    The `depends` and `depend_vars` arguments are variadic lists of values
-    that specify the dependencies of this particular target task in relation to
-    other tasks.
-
-    TODO: map_type_modifier values iterator and mapper.
-  }];
-
-  let arguments = (ins Optional<I1>:$if_expr,
-                       Optional<AnyInteger>:$device,
-                       OptionalAttr<TaskDependArrayAttr>:$depends,
-                       Variadic<OpenMP_PointerLikeType>:$depend_vars,
-                       UnitAttr:$nowait,
-                       Variadic<AnyType>:$map_operands);
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const TargetEnterExitUpdateDataClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist(`if` `(` $if_expr `:` type($if_expr) `)`
-    | `device` `(` $device `:` type($device) `)`
-    | `nowait` $nowait
-    | `map_entries` `(` $map_operands `:` type($map_operands) `)`
-    | `depend` `(` custom<DependVarList>($depend_vars, type($depend_vars), $depends) `)`
-    ) attr-dict
-   }];
-
   let hasVerifier = 1;
 }
 
@@ -1499,59 +1065,35 @@ def TargetExitDataOp: OpenMP_Op<"target_exit_data",
 // 2.14.6 target update Construct
 //===---------------------------------------------------------------------===//
 
-def TargetUpdateOp: OpenMP_Op<"target_update", [AttrSizedOperandSegments,
-                                                MapClauseOwningOpInterface]>{
-  let  summary = "target update construct";
+def TargetUpdateOp: OpenMP_Op<"target_update", [AttrSizedOperandSegments], [
+    // TODO: Sort clauses alphabetically.
+    OpenMP_IfClause, OpenMP_DeviceClause, OpenMP_DependClause,
+    OpenMP_NowaitClause, OpenMP_MapClause
+  ]> {
+  let summary = "target update construct";
   let description = [{
     The target update directive makes the corresponding list items in the device
     data environment consistent with their original list items, according to the
     specified motion clauses. The target update construct is a stand-alone
     directive.
 
-    The optional $if_expr 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 target
     region runs on a device, if it is 0 then the target region is executed
     on the host device.
 
-    The optional $device parameter specifies the device number for the
-    target region.
-
-    The optional $nowait eliminates the implicit barrier so the parent
-    task can make progress even if the target task is not yet completed.
-
     We use `MapInfoOp` to model the motion clauses and their modifiers. Even
     though the spec differentiates between map-types & map-type-modifiers vs.
-    motion-clauses & motion-modifiers, the motion clauses and their modifiers are
-    a subset of map types and their modifiers. The subset relation is handled in
-    during verification to make sure the restrictions for target update are
-    respected.
-
-    The `depends` and `depend_vars` arguments are variadic lists of values
-    that specify the dependencies of this particular target task in relation to
-    other tasks.
-
-  }];
-
-  let arguments = (ins Optional<I1>:$if_expr,
-                       Optional<AnyInteger>:$device,
-                       OptionalAttr<TaskDependArrayAttr>:$depends,
-                       Variadic<OpenMP_PointerLikeType>:$depend_vars,
-                       UnitAttr:$nowait,
-                       Variadic<OpenMP_PointerLikeType>:$map_operands);
+    motion-clauses & motion-modifiers, the motion clauses and their modifiers
+    are a subset of map types and their modifiers. The subset relation is
+    handled in during verification to make sure the restrictions for target
+    update are respected.
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const TargetEnterExitUpdateDataClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist(`if` `(` $if_expr `:` type($if_expr) `)`
-    | `device` `(` $device `:` type($device) `)`
-    | `nowait` $nowait
-    | `motion_entries` `(` $map_operands `:` type($map_operands) `)`
-    | `depend` `(` custom<DependVarList>($depend_vars, type($depend_vars), $depends) `)`
-    ) attr-dict
-   }];
-
   let hasVerifier = 1;
 }
 
@@ -1559,73 +1101,31 @@ def TargetUpdateOp: OpenMP_Op<"target_update", [AttrSizedOperandSegments,
 // 2.14.5 target construct
 //===----------------------------------------------------------------------===//
 
-def TargetOp : OpenMP_Op<"target", [IsolatedFromAbove, MapClauseOwningOpInterface,
-                                   OutlineableOpenMPOpInterface, AttrSizedOperandSegments]> {
+def TargetOp : OpenMP_Op<"target", [
+    AttrSizedOperandSegments, IsolatedFromAbove, OutlineableOpenMPOpInterface
+  ], [
+    // TODO: Complete clause list (allocate, defaultmap, in_reduction,
+    // uses_allocators).
+    // TODO: Sort clauses alphabetically.
+    OpenMP_IfClause, OpenMP_DeviceClause, OpenMP_ThreadLimitClause,
+    OpenMP_DependClause, OpenMP_NowaitClause, OpenMP_IsDevicePtrClause,
+    OpenMP_HasDeviceAddrClause, OpenMP_MapClause, OpenMP_PrivateClause
+  ], singleRegion = true> {
   let summary = "target construct";
   let description = [{
     The target construct includes a region of code which is to be executed
     on a device.
 
-    The optional $if_expr 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 target
     region runs on a device, if it is 0 then the target region is executed on the
     host device.
-
-    The optional $device parameter specifies the device number for the target region.
-
-    The optional $thread_limit specifies the limit on the number of threads
-
-    The optional $nowait eliminates the implicit barrier so the parent task can make progress
-    even if the target task is not yet completed.
-
-    The `depends` and `depend_vars` arguments are variadic lists of values
-    that specify the dependencies of this particular target task in relation to
-    other tasks.
-
-    The optional $is_device_ptr indicates list items are device pointers.
-
-    The optional $has_device_addr indicates that list items already have device
-    addresses, so they may be directly accessed from the target device. This
-    includes array sections.
-
-    The optional $map_operands maps data from the task’s environment to the
-    device environment.
-
-    TODO:  defaultmap, in_reduction
-
-  }];
-
-  let arguments = (ins Optional<I1>:$if_expr,
-                       Optional<AnyInteger>:$device,
-                       Optional<AnyInteger>:$thread_limit,
-                       OptionalAttr<TaskDependArrayAttr>:$depends,
-                       Variadic<OpenMP_PointerLikeType>:$depend_vars,
-                       UnitAttr:$nowait,
-                       Variadic<OpenMP_PointerLikeType>:$is_device_ptr,
-                       Variadic<OpenMP_PointerLikeType>:$has_device_addr,
-                       Variadic<AnyType>:$map_operands,
-                       Variadic<AnyType>:$private_vars,
-                       OptionalAttr<SymbolRefArrayAttr>:$privatizers);
-
-  let regions = (region AnyRegion:$region);
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const TargetClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    oilist( `if` `(` $if_expr `)`
-    | `device` `(` $device `:` type($device) `)`
-    | `thread_limit` `(` $thread_limit `:` type($thread_limit) `)`
-    | `nowait` $nowait
-    | `is_device_ptr` `(` $is_device_ptr `:` type($is_device_ptr) `)`
-    | `has_device_addr` `(` $has_device_addr `:` type($has_device_addr) `)`
-    | `map_entries` `(` custom<MapEntries>($map_operands, type($map_operands)) `)`
-    | `private` `(` custom<PrivateList>($private_vars, type($private_vars), $privatizers) `)`
-    | `depend` `(` custom<DependVarList>($depend_vars, type($depend_vars), $depends) `)`
-    ) $region attr-dict
-  }];
-
   let hasVerifier = 1;
 }
 
@@ -1633,57 +1133,50 @@ def TargetOp : OpenMP_Op<"target", [IsolatedFromAbove, MapClauseOwningOpInterfac
 //===----------------------------------------------------------------------===//
 // 2.16 master Construct
 //===----------------------------------------------------------------------===//
-def MasterOp : OpenMP_Op<"master"> {
+def MasterOp : OpenMP_Op<"master", singleRegion = true> {
   let summary = "master construct";
   let description = [{
     The master construct specifies a structured block that is executed by
     the master thread of the team.
   }];
 
-  let regions = (region AnyRegion:$region);
-
   let assemblyFormat = "$region attr-dict";
 }
 
 //===----------------------------------------------------------------------===//
 // 2.17.1 critical Construct
 //===----------------------------------------------------------------------===//
-def CriticalDeclareOp : OpenMP_Op<"critical.declare", [Symbol]> {
+def CriticalDeclareOp : OpenMP_Op<"critical.declare", [Symbol], [
+    OpenMP_CriticalNameClause, OpenMP_HintClause
+  ]> {
   let summary = "declares a named critical section.";
-
   let description = [{
     Declares a named critical section.
-
-    The name can be used in critical constructs in the dialect.
-  }];
-
-  let arguments = (ins SymbolNameAttr:$sym_name,
-                       DefaultValuedAttr<I64Attr, "0">:$hint_val);
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const CriticalClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    $sym_name oilist(`hint` `(` custom<SynchronizationHint>($hint_val) `)`)
-    attr-dict
-  }];
   let hasVerifier = 1;
 }
 
 
-def CriticalOp : OpenMP_Op<"critical",
-    [DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
+def CriticalOp : OpenMP_Op<"critical", [
+    DeclareOpInterfaceMethods<SymbolUserOpInterface>
+  ], singleRegion = 1> {
   let summary = "critical construct";
   let description = [{
     The critical construct imposes a restriction on the associated structured
     block (region) to be executed by only a single thread at a time.
+
+    The optional `name` argument of critical constructs is used to identify
+    them. Unnamed critical constructs behave as though an identical name was
+    specified.
   }];
 
   let arguments = (ins OptionalAttr<FlatSymbolRefAttr>:$name);
 
-  let regions = (region AnyRegion:$region);
-
   let assemblyFormat = [{
     (`(` $name^ `)`)? $region attr-dict
   }];
@@ -1707,62 +1200,34 @@ def BarrierOp : OpenMP_Op<"barrier"> {
 // [5.1] 2.19.9 ordered Construct
 //===----------------------------------------------------------------------===//
 
-def OrderedOp : OpenMP_Op<"ordered"> {
+def OrderedOp : OpenMP_Op<"ordered", clauses = [OpenMP_DoacrossClause]> {
   let summary = "ordered construct without region";
   let description = [{
     The ordered construct without region is a stand-alone directive that
-    specifies cross-iteration dependences in a doacross loop nest.
-
-    The `depend_type_val` attribute refers to either the DEPEND(SOURCE) clause
-    or the DEPEND(SINK: vec) clause.
-
-    The `num_loops_val` attribute specifies the number of loops in the doacross
-    nest.
-
-    The `depend_vec_vars` is a variadic list of operands that specifies the index
-    of the loop iterator in the doacross nest for the DEPEND(SOURCE) clause or
-    the index of the element of "vec" for the DEPEND(SINK: vec) clause. It
-    contains the operands in multiple "vec" when multiple DEPEND(SINK: vec)
-    clauses exist in one ORDERED directive.
-  }];
-
-  let arguments = (ins OptionalAttr<ClauseDependAttr>:$depend_type_val,
-             ConfinedAttr<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$num_loops_val,
-             Variadic<AnyType>:$depend_vec_vars);
+    specifies cross-iteration dependencies in a doacross loop nest.
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const OrderedOpClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{
-    ( `depend_type` `` $depend_type_val^ )?
-    ( `depend_vec` `(` $depend_vec_vars^ `:` type($depend_vec_vars) `)` )?
-    attr-dict
-  }];
   let hasVerifier = 1;
 }
 
-def OrderedRegionOp : OpenMP_Op<"ordered.region"> {
+def OrderedRegionOp : OpenMP_Op<"ordered.region", clauses = [
+    OpenMP_ParallelizationLevelClause
+  ], singleRegion = true> {
   let summary = "ordered construct with region";
   let description = [{
     The ordered construct with region specifies a structured block in a
     worksharing-loop, SIMD, or worksharing-loop SIMD region that is executed in
     the order of the loop iterations.
-
-    The `simd` attribute corresponds to the SIMD clause specified. If it is not
-    present, it behaves as if the THREADS clause is specified or no clause is
-    specified.
-  }];
-
-  let arguments = (ins UnitAttr:$simd);
-
-  let regions = (region AnyRegion:$region);
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const OrderedRegionClauseOps &">:$clauses)>
   ];
 
-  let assemblyFormat = [{ ( `simd` $simd^ )? $region attr-dict}];
   let hasVerifier = 1;
 }
 
@@ -1770,17 +1235,20 @@ def OrderedRegionOp : OpenMP_Op<"ordered.region"> {
 // 2.17.5 taskwait Construct
 //===----------------------------------------------------------------------===//
 
-def TaskwaitOp : OpenMP_Op<"taskwait"> {
+def TaskwaitOp : OpenMP_Op<"taskwait", clauses = [
+    // TODO: Complete clause list (depend, nowait).
+  ]> {
   let summary = "taskwait construct";
   let description = [{
     The taskwait construct specifies a wait on the completion of child tasks
     of the current task.
-  }];
+  }] # clausesDescription;
 
   let builders = [
     OpBuilder<(ins CArg<"const TaskwaitClauseOps &">:$clauses)>
   ];
 
+  // TODO: Remove overriden `assemblyFormat` once a clause is added.
   let assemblyFormat = "attr-dict";
 }
 
@@ -1795,37 +1263,31 @@ def TaskwaitOp : OpenMP_Op<"taskwait"> {
 // value of the clause) here decomposes handling of this construct into a
 // two-step process.
 
-def AtomicReadOp : OpenMP_Op<"atomic.read", [AllTypesMatch<["x", "v"]>,
-                                            AtomicReadOpInterface]> {
-
+def AtomicReadOp : OpenMP_Op<"atomic.read", [
+    AllTypesMatch<["x", "v"]>, AtomicReadOpInterface
+  ], [
+    OpenMP_HintClause, OpenMP_MemoryOrderClause
+  ]> {
   let summary = "performs an atomic read";
-
   let description = [{
     This operation performs an atomic read.
 
     The operand `x` is the address from where the value is atomically read.
     The operand `v` is the address where the value is stored after reading.
+  }] # clausesDescription;
 
-    `hint` is the value of hint (as specified in the hint clause). It is a
-    compile time constant. As the name suggests, this is just a hint for
-    optimization.
+  let arguments = !con((ins OpenMP_PointerLikeType:$x,
+                            OpenMP_PointerLikeType:$v,
+                            TypeAttr:$element_type), clausesArgs);
 
-    `memory_order` indicates the memory ordering behavior of the construct. It
-    can be one of `seq_cst`, `acquire` or `relaxed`.
-  }];
-
-  let arguments = (ins OpenMP_PointerLikeType:$x,
-                       OpenMP_PointerLikeType:$v,
-                       TypeAttr:$element_type,
-                       DefaultValuedOptionalAttr<I64Attr, "0">:$hint_val,
-                       OptionalAttr<MemoryOrderKindAttr>:$memory_order_val);
+  // Override clause-based assemblyFormat.
   let assemblyFormat = [{
     $v `=` $x
     oilist( `memory_order` `(` custom<ClauseAttr>($memory_order_val) `)`
           | `hint` `(` custom<SynchronizationHint>($hint_val) `)`)
     `:` type($x) `,` $element_type attr-dict
   }];
-  let hasVerifier = 1;
+
   let extraClassDeclaration = [{
     /// The number of variable operands.
     unsigned getNumVariableOperands() {
@@ -1839,13 +1301,15 @@ def AtomicReadOp : OpenMP_Op<"atomic.read", [AllTypesMatch<["x", "v"]>,
       assert(i < 2 && "invalid index position for an operand");
       return i == 0 ? getX() : getV();
     }
-  }];
-}
+  }] # clausesExtraClassDeclaration;
 
-def AtomicWriteOp : OpenMP_Op<"atomic.write", [AtomicWriteOpInterface]> {
+  let hasVerifier = 1;
+}
 
+def AtomicWriteOp : OpenMP_Op<"atomic.write", [AtomicWriteOpInterface], [
+    OpenMP_HintClause, OpenMP_MemoryOrderClause
+  ]> {
   let summary = "performs an atomic write";
-
   let description = [{
     This operation performs an atomic write.
 
@@ -1853,19 +1317,12 @@ def AtomicWriteOp : OpenMP_Op<"atomic.write", [AtomicWriteOpInterface]> {
     written w.r.t. multiple threads. The evaluation of `expr` need not be
     atomic w.r.t. the write to address. In general, the type(x) must
     dereference to type(expr).
+  }] # clausesDescription;
 
-    `hint` is the value of hint (as specified in the hint clause). It is a
-    compile time constant. As the name suggests, this is just a hint for
-    optimization.
+  let arguments = !con((ins OpenMP_PointerLikeType:$x,
+                            AnyType:$expr), clausesArgs);
 
-    `memory_order` indicates the memory ordering behavior of the construct. It
-    can be one of `seq_cst`, `release` or `relaxed`.
-  }];
-
-  let arguments = (ins OpenMP_PointerLikeType:$x,
-                       AnyType:$expr,
-                       DefaultValuedOptionalAttr<I64Attr, "0">:$hint_val,
-                       OptionalAttr<MemoryOrderKindAttr>:$memory_order_val);
+  // Override clause-based assemblyFormat.
   let assemblyFormat = [{
     $x `=` $expr
     oilist( `hint` `(` custom<SynchronizationHint>($hint_val) `)`
@@ -1873,7 +1330,7 @@ def AtomicWriteOp : OpenMP_Op<"atomic.write", [AtomicWriteOpInterface]> {
     `:` type($x) `,` type($expr)
     attr-dict
   }];
-  let hasVerifier = 1;
+
   let extraClassDeclaration = [{
     /// The number of variable operands.
     unsigned getNumVariableOperands() {
@@ -1887,16 +1344,18 @@ def AtomicWriteOp : OpenMP_Op<"atomic.write", [AtomicWriteOpInterface]> {
       assert(i < 2 && "invalid index position for an operand");
       return i == 0 ? getX() : getExpr();
     }
-  }];
-}
+  }] # clausesExtraClassDeclaration;
 
-def AtomicUpdateOp : OpenMP_Op<"atomic.update",
-                               [SingleBlockImplicitTerminator<"YieldOp">,
-                                RecursiveMemoryEffects,
-                                AtomicUpdateOpInterface]> {
+  let hasVerifier = 1;
+}
 
+def AtomicUpdateOp : OpenMP_Op<"atomic.update", [
+    AtomicUpdateOpInterface, RecursiveMemoryEffects,
+    SingleBlockImplicitTerminator<"YieldOp">
+  ], [
+    OpenMP_HintClause, OpenMP_MemoryOrderClause
+  ], singleRegion = 1> {
   let summary = "performs an atomic update";
-
   let description = [{
     This operation performs an atomic update.
 
@@ -1904,12 +1363,6 @@ def AtomicUpdateOp : OpenMP_Op<"atomic.update",
     Standard (OpenMP 5.0, section 2.17.7). It is the address of the variable
     that is being updated. `x` is atomically read/written.
 
-    `hint` is the value of hint (as used in the hint clause). It is a compile
-    time constant. As the name suggests, this is just a hint for optimization.
-
-    `memory_order` indicates the memory ordering behavior of the construct. It
-    can be one of `seq_cst`, `release` or `relaxed`.
-
     The region describes how to update the value of `x`. It takes the value at
     `x` as an input and must yield the updated value. Only the update to `x` is
     atomic. Generally the region must have only one instruction, but can
@@ -1924,22 +1377,22 @@ def AtomicUpdateOp : OpenMP_Op<"atomic.update",
     the core update operation is directly translated like regular operations by
     the host dialect. The front-end must handle semantic checks for allowed
     operations.
-  }];
+  }] # clausesDescription;
+
+  let arguments = !con((ins Arg<OpenMP_PointerLikeType,
+                                "Address of variable to be updated",
+                                [MemRead, MemWrite]>:$x), clausesArgs);
 
-  let arguments = (ins Arg<OpenMP_PointerLikeType,
-                           "Address of variable to be updated",
-                           [MemRead, MemWrite]>:$x,
-                       DefaultValuedOptionalAttr<I64Attr, "0">:$hint_val,
-                       OptionalAttr<MemoryOrderKindAttr>:$memory_order_val);
+  // Override region definition.
   let regions = (region SizedRegion<1>:$region);
+
+  // Override clause-based assemblyFormat.
   let assemblyFormat = [{
     oilist( `memory_order` `(` custom<ClauseAttr>($memory_order_val) `)`
           | `hint` `(` custom<SynchronizationHint>($hint_val) `)`)
     $x `:` type($x) $region attr-dict
   }];
-  let hasVerifier = 1;
-  let hasRegionVerifier = 1;
-  let hasCanonicalizeMethod = 1;
+
   let extraClassDeclaration = [{
     /// The number of variable operands.
     unsigned getNumVariableOperands() {
@@ -1952,24 +1405,24 @@ def AtomicUpdateOp : OpenMP_Op<"atomic.update",
       assert(i == 0 && "invalid index position for an operand");
       return getX();
     }
-  }];
+  }] # clausesExtraClassDeclaration;
+
+  let hasVerifier = 1;
+  let hasRegionVerifier = 1;
+  let hasCanonicalizeMethod = 1;
 }
 
-def AtomicCaptureOp : OpenMP_Op<"atomic.capture",
-    [SingleBlockImplicitTerminator<"TerminatorOp">,
-     RecursiveMemoryEffects, AtomicCaptureOpInterface]> {
+def AtomicCaptureOp : OpenMP_Op<"atomic.capture", [
+    AtomicCaptureOpInterface, RecursiveMemoryEffects,
+    SingleBlockImplicitTerminator<"TerminatorOp">
+  ], [
+    OpenMP_HintClause, OpenMP_MemoryOrderClause
+  ], singleRegion = 1> {
   let summary = "performs an atomic capture";
   let description = [{
     This operation performs an atomic capture.
 
-    `hint` is the value of hint (as used in the hint clause). It is a compile
-    time constant. As the name suggests, this is just a hint for optimization.
-
-    `memory_order` indicates the memory ordering behavior of the construct. It
-    can be one of `seq_cst`, `acq_rel`, `release`, `acquire` or `relaxed`.
-
     The region has the following allowed forms:
-
     ```
       omp.atomic.capture {
         omp.atomic.update ...
@@ -1989,19 +1442,11 @@ def AtomicCaptureOp : OpenMP_Op<"atomic.capture",
         omp.terminator
       }
     ```
+  }] # clausesDescription;
 
-  }];
-
-  let arguments = (ins DefaultValuedOptionalAttr<I64Attr, "0">:$hint_val,
-                       OptionalAttr<MemoryOrderKindAttr>:$memory_order_val);
+  // Override region definition.
   let regions = (region SizedRegion<1>:$region);
-  let assemblyFormat = [{
-    oilist(`memory_order` `(` custom<ClauseAttr>($memory_order_val) `)`
-          |`hint` `(` custom<SynchronizationHint>($hint_val) `)`)
-    $region attr-dict
-  }];
-  let hasRegionVerifier = 1;
-  let hasVerifier = 1;
+
   let extraClassDeclaration = [{
     /// Returns the `atomic.read` operation inside the region, if any.
     /// Otherwise, it returns nullptr.
@@ -2014,7 +1459,10 @@ def AtomicCaptureOp : OpenMP_Op<"atomic.capture",
     /// Returns the `atomic.update` operation inside the region, if any.
     /// Otherwise, it returns nullptr.
     AtomicUpdateOp getAtomicUpdateOp();
-  }];
+  }] # clausesExtraClassDeclaration;
+
+  let hasRegionVerifier = 1;
+  let hasVerifier = 1;
 }
 
 //===----------------------------------------------------------------------===//
@@ -2064,34 +1512,39 @@ def ThreadprivateOp : OpenMP_Op<"threadprivate",
 //===----------------------------------------------------------------------===//
 // 2.18.1 Cancel Construct
 //===----------------------------------------------------------------------===//
-def CancelOp : OpenMP_Op<"cancel"> {
+def CancelOp : OpenMP_Op<"cancel", clauses = [
+    OpenMP_CancelDirectiveNameClause, OpenMP_IfClause
+  ]> {
   let summary = "cancel directive";
   let description = [{
     The cancel construct activates cancellation of the innermost enclosing
     region of the type specified.
-  }];
-  let arguments = (ins CancellationConstructTypeAttr:$cancellation_construct_type_val,
-                       Optional<I1>:$if_expr);
-  let assemblyFormat = [{ `cancellation_construct_type` `(`
-                          custom<ClauseAttr>($cancellation_construct_type_val) `)`
-                          ( `if` `(` $if_expr^ `)` )? attr-dict}];
+  }] # clausesDescription;
+
+  let builders = [
+    OpBuilder<(ins CArg<"const CancelClauseOps &">:$clauses)>
+  ];
+
   let hasVerifier = 1;
 }
 
 //===----------------------------------------------------------------------===//
 // 2.18.2 Cancellation Point Construct
 //===----------------------------------------------------------------------===//
-def CancellationPointOp : OpenMP_Op<"cancellation_point"> {
+def CancellationPointOp : OpenMP_Op<"cancellation_point", clauses = [
+    OpenMP_CancelDirectiveNameClause
+  ]> {
   let summary = "cancellation point directive";
   let description = [{
     The cancellation point construct introduces a user-defined cancellation
     point at which implicit or explicit tasks check if cancellation of the
     innermost enclosing region of the type specified has been activated.
-  }];
-  let arguments = (ins CancellationConstructTypeAttr:$cancellation_construct_type_val);
-  let assemblyFormat = [{ `cancellation_construct_type` `(`
-                           custom<ClauseAttr>($cancellation_construct_type_val) `)`
-                           attr-dict}];
+  }] # clausesDescription;
+
+  let builders = [
+    OpBuilder<(ins CArg<"const CancellationPointClauseOps &">:$clauses)>
+  ];
+
   let hasVerifier = 1;
 }
 
@@ -2099,10 +1552,9 @@ def CancellationPointOp : OpenMP_Op<"cancellation_point"> {
 // 2.19.5.7 declare reduction Directive
 //===----------------------------------------------------------------------===//
 
-def DeclareReductionOp : OpenMP_Op<"declare_reduction", [Symbol,
-                                                         IsolatedFromAbove]> {
+def DeclareReductionOp : OpenMP_Op<"declare_reduction", [IsolatedFromAbove,
+                                                         Symbol]> {
   let summary = "declares a reduction kind";
-
   let description = [{
     Declares an OpenMP reduction kind. This requires two mandatory and two
     optional regions.
@@ -2169,7 +1621,7 @@ def ReductionOp : OpenMP_Op<"reduction"> {
     updated immediately.
   }];
 
-  let arguments= (ins AnyType:$operand, OpenMP_PointerLikeType:$accumulator);
+  let arguments = (ins AnyType:$operand, OpenMP_PointerLikeType:$accumulator);
   let assemblyFormat = [{
     $operand `,` $accumulator attr-dict `:` type($operand) `,` type($accumulator)
   }];
diff --git a/mlir/lib/Conversion/SCFToOpenMP/SCFToOpenMP.cpp b/mlir/lib/Conversion/SCFToOpenMP/SCFToOpenMP.cpp
index d6f85451ee5d3..3220140400439 100644
--- a/mlir/lib/Conversion/SCFToOpenMP/SCFToOpenMP.cpp
+++ b/mlir/lib/Conversion/SCFToOpenMP/SCFToOpenMP.cpp
@@ -444,12 +444,13 @@ struct ParallelOpLowering : public OpRewritePattern<scf::ParallelOp> {
     // Create the parallel wrapper.
     auto ompParallel = rewriter.create<omp::ParallelOp>(
         loc,
-        /* if_expr_var = */ Value{},
+        /* if_expr = */ Value{},
         /* num_threads_var = */ numThreadsVar,
         /* allocate_vars = */ llvm::SmallVector<Value>{},
         /* allocators_vars = */ llvm::SmallVector<Value>{},
         /* reduction_vars = */ llvm::SmallVector<Value>{},
         /* reductions = */ ArrayAttr{},
+        /* byref = */ nullptr,
         /* proc_bind_val = */ omp::ClauseProcBindKindAttr{},
         /* private_vars = */ ValueRange(),
         /* privatizers = */ nullptr);
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index e016a326ecc78..42adf6298cb6b 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -1002,8 +1002,9 @@ parseMapEntries(OpAsmParser &parser,
   OpAsmParser::UnresolvedOperand blockArg;
   Type argType;
   auto parseEntries = [&]() -> ParseResult {
-    if (parser.parseOperand(arg) || parser.parseArrow() ||
-        parser.parseOperand(blockArg))
+    if (parser.parseOperand(arg))
+      return failure();
+    if (succeeded(parser.parseOptionalArrow()) && parser.parseOperand(blockArg))
       return failure();
     mapOperands.push_back(arg);
     return success();
@@ -1031,12 +1032,18 @@ parseMapEntries(OpAsmParser &parser,
 static void printMapEntries(OpAsmPrinter &p, Operation *op,
                             OperandRange mapOperands,
                             TypeRange mapOperandTypes) {
-  auto &region = op->getRegion(0);
+  // Get pointer to the region if this is an omp.target, because printing map
+  // clauses for that operation has to also show the correspondence of each
+  // variable to the corresponding block argument.
+  Block *entryBlock = isa<TargetOp>(op) ? &op->getRegion(0).front() : nullptr;
   unsigned argIndex = 0;
 
   for (const auto &mapOp : mapOperands) {
-    const auto &blockArg = region.front().getArgument(argIndex);
-    p << mapOp << " -> " << blockArg;
+    p << mapOp;
+    if (entryBlock) {
+      const auto &blockArg = entryBlock->getArgument(argIndex);
+      p << " -> " << blockArg;
+    }
     argIndex++;
     if (argIndex < mapOperands.size())
       p << ", ";
@@ -1327,11 +1334,11 @@ LogicalResult TargetOp::verify() {
 void ParallelOp::build(OpBuilder &builder, OperationState &state,
                        ArrayRef<NamedAttribute> attributes) {
   ParallelOp::build(
-      builder, state, /*if_expr_var=*/nullptr, /*num_threads_var=*/nullptr,
+      builder, state, /*if_expr=*/nullptr, /*num_threads_var=*/nullptr,
       /*allocate_vars=*/ValueRange(), /*allocators_vars=*/ValueRange(),
-      /*reduction_vars=*/ValueRange(), /*reductions=*/nullptr,
+      /*reduction_vars=*/ValueRange(), /*reductions=*/nullptr, /*byref=*/false,
       /*proc_bind_val=*/nullptr, /*private_vars=*/ValueRange(),
-      /*privatizers=*/nullptr, /*byref=*/false);
+      /*privatizers=*/nullptr);
   state.addAttributes(attributes);
 }
 
@@ -1342,8 +1349,8 @@ void ParallelOp::build(OpBuilder &builder, OperationState &state,
       builder, state, clauses.ifVar, clauses.numThreadsVar,
       clauses.allocateVars, clauses.allocatorVars, clauses.reductionVars,
       makeArrayAttr(ctx, clauses.reductionDeclSymbols),
-      clauses.procBindKindAttr, clauses.privateVars,
-      makeArrayAttr(ctx, clauses.privatizers), clauses.reductionByRefAttr);
+      clauses.reductionByRefAttr, clauses.procBindKindAttr, clauses.privateVars,
+      makeArrayAttr(ctx, clauses.privatizers));
 }
 
 template <typename OpType>
@@ -1481,8 +1488,8 @@ void SectionsOp::build(OpBuilder &builder, OperationState &state,
   // TODO Store clauses in op: reductionByRefAttr, privateVars, privatizers.
   SectionsOp::build(builder, state, clauses.reductionVars,
                     makeArrayAttr(ctx, clauses.reductionDeclSymbols),
-                    clauses.allocateVars, clauses.allocatorVars,
-                    clauses.nowaitAttr);
+                    clauses.reductionByRefAttr, clauses.allocateVars,
+                    clauses.allocatorVars, clauses.nowaitAttr);
 }
 
 LogicalResult SectionsOp::verify() {
@@ -1564,9 +1571,9 @@ void WsloopOp::build(OpBuilder &builder, OperationState &state,
                      ArrayRef<NamedAttribute> attributes) {
   build(builder, state, /*linear_vars=*/ValueRange(),
         /*linear_step_vars=*/ValueRange(), /*reduction_vars=*/ValueRange(),
-        /*reductions=*/nullptr, /*schedule_val=*/nullptr,
+        /*reductions=*/nullptr, /*byref=*/false, /*schedule_val=*/nullptr,
         /*schedule_chunk_var=*/nullptr, /*schedule_modifier=*/nullptr,
-        /*simd_modifier=*/false, /*nowait=*/false, /*byref=*/false,
+        /*simd_modifier=*/false, /*nowait=*/false,
         /*ordered_val=*/nullptr, /*order_val=*/nullptr);
   state.addAttributes(attributes);
 }
@@ -1576,12 +1583,13 @@ void WsloopOp::build(OpBuilder &builder, OperationState &state,
   MLIRContext *ctx = builder.getContext();
   // TODO: Store clauses in op: allocateVars, allocatorVars, privateVars,
   // privatizers.
-  WsloopOp::build(
-      builder, state, clauses.linearVars, clauses.linearStepVars,
-      clauses.reductionVars, makeArrayAttr(ctx, clauses.reductionDeclSymbols),
-      clauses.scheduleValAttr, clauses.scheduleChunkVar,
-      clauses.scheduleModAttr, clauses.scheduleSimdAttr, clauses.nowaitAttr,
-      clauses.reductionByRefAttr, clauses.orderedAttr, clauses.orderAttr);
+  WsloopOp::build(builder, state, clauses.linearVars, clauses.linearStepVars,
+                  clauses.reductionVars,
+                  makeArrayAttr(ctx, clauses.reductionDeclSymbols),
+                  clauses.reductionByRefAttr, clauses.scheduleValAttr,
+                  clauses.scheduleChunkVar, clauses.scheduleModAttr,
+                  clauses.scheduleSimdAttr, clauses.nowaitAttr,
+                  clauses.orderedAttr, clauses.orderAttr);
 }
 
 LogicalResult WsloopOp::verify() {
@@ -1609,8 +1617,8 @@ void SimdOp::build(OpBuilder &builder, OperationState &state,
   // privatizers, reductionDeclSymbols.
   SimdOp::build(builder, state, clauses.alignedVars,
                 makeArrayAttr(ctx, clauses.alignmentAttrs), clauses.ifVar,
-                clauses.nontemporalVars, clauses.orderAttr, clauses.simdlenAttr,
-                clauses.safelenAttr);
+                clauses.nontemporalVars, clauses.orderAttr, clauses.safelenAttr,
+                clauses.simdlenAttr);
 }
 
 LogicalResult SimdOp::verify() {
@@ -1833,9 +1841,10 @@ void TaskloopOp::build(OpBuilder &builder, OperationState &state,
       builder, state, clauses.ifVar, clauses.finalVar, clauses.untiedAttr,
       clauses.mergeableAttr, clauses.inReductionVars,
       makeArrayAttr(ctx, clauses.inReductionDeclSymbols), clauses.reductionVars,
-      makeArrayAttr(ctx, clauses.reductionDeclSymbols), clauses.priorityVar,
-      clauses.allocateVars, clauses.allocatorVars, clauses.grainsizeVar,
-      clauses.numTasksVar, clauses.nogroupAttr);
+      makeArrayAttr(ctx, clauses.reductionDeclSymbols),
+      clauses.reductionByRefAttr, clauses.priorityVar, clauses.allocateVars,
+      clauses.allocatorVars, clauses.grainsizeVar, clauses.numTasksVar,
+      clauses.nogroupAttr);
 }
 
 SmallVector<Value> TaskloopOp::getAllReductionVars() {
@@ -1987,7 +1996,8 @@ void LoopNestOp::gatherWrappers(
 
 void CriticalDeclareOp::build(OpBuilder &builder, OperationState &state,
                               const CriticalClauseOps &clauses) {
-  CriticalDeclareOp::build(builder, state, clauses.nameAttr, clauses.hintAttr);
+  CriticalDeclareOp::build(builder, state, clauses.criticalNameAttr,
+                           clauses.hintAttr);
 }
 
 LogicalResult CriticalDeclareOp::verify() {
@@ -2201,9 +2211,15 @@ LogicalResult AtomicCaptureOp::verifyRegions() {
 }
 
 //===----------------------------------------------------------------------===//
-// Verifier for CancelOp
+// CancelOp
 //===----------------------------------------------------------------------===//
 
+void CancelOp::build(OpBuilder &builder, OperationState &state,
+                     const CancelClauseOps &clauses) {
+  CancelOp::build(builder, state, clauses.cancelDirectiveNameAttr,
+                  clauses.ifVar);
+}
+
 LogicalResult CancelOp::verify() {
   ClauseCancellationConstructType cct = getCancellationConstructTypeVal();
   Operation *parentOp = (*this)->getParentOp();
@@ -2250,10 +2266,16 @@ LogicalResult CancelOp::verify() {
   // TODO : Add more when we support taskgroup.
   return success();
 }
+
 //===----------------------------------------------------------------------===//
-// Verifier for CancelOp
+// CancellationPointOp
 //===----------------------------------------------------------------------===//
 
+void CancellationPointOp::build(OpBuilder &builder, OperationState &state,
+                                const CancellationPointClauseOps &clauses) {
+  CancellationPointOp::build(builder, state, clauses.cancelDirectiveNameAttr);
+}
+
 LogicalResult CancellationPointOp::verify() {
   ClauseCancellationConstructType cct = getCancellationConstructTypeVal();
   Operation *parentOp = (*this)->getParentOp();
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index bfd7d65912bdb..308dddf7206c3 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -1420,7 +1420,7 @@ convertOmpParallel(omp::ParallelOp opInst, llvm::IRBuilderBase &builder,
   };
 
   llvm::Value *ifCond = nullptr;
-  if (auto ifExprVar = opInst.getIfExprVar())
+  if (auto ifExprVar = opInst.getIfExpr())
     ifCond = moduleTranslation.lookupValue(ifExprVar);
   llvm::Value *numThreads = nullptr;
   if (auto numThreadsVar = opInst.getNumThreadsVar())
diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir
index 138c2c9d418dc..b5f24bf239ec7 100644
--- a/mlir/test/Dialect/OpenMP/invalid.mlir
+++ b/mlir/test/Dialect/OpenMP/invalid.mlir
@@ -64,7 +64,7 @@ func.func @no_nested_wrapper(%lb : index, %ub : index, %step : index) {
 
 func.func @if_once(%n : i1) {
   // expected-error at +1 {{`if` clause can appear at most once in the expansion of the oilist directive}}
-  omp.parallel if(%n : i1) if(%n : i1) {
+  omp.parallel if(%n) if(%n) {
   }
 
   return
@@ -254,7 +254,7 @@ func.func @order_value(%lb : index, %ub : index, %step : index) {
 
 func.func @if_not_allowed(%lb : index, %ub : index, %step : index, %bool_var : i1) {
   // expected-error @below {{expected '{'}}
-  omp.wsloop if(%bool_var: i1) {
+  omp.wsloop if(%bool_var) {
     omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
       omp.yield
     }
@@ -2006,7 +2006,7 @@ func.func @omp_target_update_invalid_motion_type(%map1 : memref<?xi32>) {
   %mapv = omp.map.info var_ptr(%map1 : memref<?xi32>, tensor<?xi32>) map_clauses(exit_release_or_enter_alloc) capture(ByRef) -> memref<?xi32> {name = ""}
 
   // expected-error @below {{at least one of to or from map types must be specified, other map types are not permitted}}
-  omp.target_update motion_entries(%mapv : memref<?xi32>)
+  omp.target_update map_entries(%mapv : memref<?xi32>)
   return
 }
 
@@ -2016,7 +2016,7 @@ func.func @omp_target_update_invalid_motion_type_2(%map1 : memref<?xi32>) {
   %mapv = omp.map.info var_ptr(%map1 : memref<?xi32>, tensor<?xi32>) map_clauses(delete) capture(ByRef) -> memref<?xi32> {name = ""}
 
   // expected-error @below {{at least one of to or from map types must be specified, other map types are not permitted}}
-  omp.target_update motion_entries(%mapv : memref<?xi32>)
+  omp.target_update map_entries(%mapv : memref<?xi32>)
   return
 }
 
@@ -2026,7 +2026,7 @@ func.func @omp_target_update_invalid_motion_modifier(%map1 : memref<?xi32>) {
   %mapv = omp.map.info var_ptr(%map1 : memref<?xi32>, tensor<?xi32>) map_clauses(always, to) capture(ByRef) -> memref<?xi32> {name = ""}
 
   // expected-error @below {{present, mapper and iterator map type modifiers are permitted}}
-  omp.target_update motion_entries(%mapv : memref<?xi32>)
+  omp.target_update map_entries(%mapv : memref<?xi32>)
   return
 }
 
@@ -2036,7 +2036,7 @@ func.func @omp_target_update_invalid_motion_modifier_2(%map1 : memref<?xi32>) {
   %mapv = omp.map.info var_ptr(%map1 : memref<?xi32>, tensor<?xi32>) map_clauses(close, to) capture(ByRef) -> memref<?xi32> {name = ""}
 
   // expected-error @below {{present, mapper and iterator map type modifiers are permitted}}
-  omp.target_update motion_entries(%mapv : memref<?xi32>)
+  omp.target_update map_entries(%mapv : memref<?xi32>)
   return
 }
 
@@ -2046,7 +2046,7 @@ func.func @omp_target_update_invalid_motion_modifier_3(%map1 : memref<?xi32>) {
   %mapv = omp.map.info var_ptr(%map1 : memref<?xi32>, tensor<?xi32>) map_clauses(implicit, to) capture(ByRef) -> memref<?xi32> {name = ""}
 
   // expected-error @below {{present, mapper and iterator map type modifiers are permitted}}
-  omp.target_update motion_entries(%mapv : memref<?xi32>)
+  omp.target_update map_entries(%mapv : memref<?xi32>)
   return
 }
 
@@ -2056,7 +2056,7 @@ func.func @omp_target_update_invalid_motion_modifier_4(%map1 : memref<?xi32>) {
   %mapv = omp.map.info var_ptr(%map1 : memref<?xi32>, tensor<?xi32>) map_clauses(implicit, tofrom) capture(ByRef) -> memref<?xi32> {name = ""}
 
   // expected-error @below {{either to or from map types can be specified, not both}}
-  omp.target_update motion_entries(%mapv : memref<?xi32>)
+  omp.target_update map_entries(%mapv : memref<?xi32>)
   return
 }
 
@@ -2067,7 +2067,7 @@ func.func @omp_target_update_invalid_motion_modifier_5(%map1 : memref<?xi32>) {
   %mapv2 = omp.map.info var_ptr(%map1 : memref<?xi32>, tensor<?xi32>) map_clauses(from) capture(ByRef) -> memref<?xi32> {name = ""}
 
   // expected-error @below {{either to or from map types can be specified, not both}}
-  omp.target_update motion_entries(%mapv, %mapv2 : memref<?xi32>, memref<?xi32>)
+  omp.target_update map_entries(%mapv, %mapv2 : memref<?xi32>, memref<?xi32>)
   return
 }
 llvm.mlir.global internal @_QFsubEx() : i32
@@ -2077,7 +2077,7 @@ llvm.mlir.global internal @_QFsubEx() : i32
 func.func @omp_target_update_data_depend(%a: memref<?xi32>) {
   %0 = omp.map.info var_ptr(%a: memref<?xi32>, tensor<?xi32>) map_clauses(to) capture(ByRef) -> memref<?xi32>
   // expected-error @below {{op expected as many depend values as depend variables}}
-  omp.target_update motion_entries(%0: memref<?xi32> ) {operandSegmentSizes = array<i32: 0, 0, 1, 0>}
+  omp.target_update map_entries(%0: memref<?xi32> ) {operandSegmentSizes = array<i32: 0, 0, 1, 0>}
   return
 }
 
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 828c9d2c3b84f..6ac8369f065c1 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -134,16 +134,16 @@ func.func @omp_parallel_pretty(%data_var : memref<i32>, %if_cond : i1, %num_thre
  }
 
  // CHECK: omp.parallel
- // CHECK-NEXT: omp.parallel if(%{{.*}} : i1)
+ // CHECK-NEXT: omp.parallel if(%{{.*}})
  omp.parallel {
-   omp.parallel if(%if_cond: i1) {
+   omp.parallel if(%if_cond) {
      omp.terminator
    }
    omp.terminator
  }
 
  // CHECK omp.parallel if(%{{.*}}) num_threads(%{{.*}} : i32) private(%{{.*}} : memref<i32>) proc_bind(close)
- omp.parallel num_threads(%num_threads : i32) if(%if_cond: i1) proc_bind(close) {
+ omp.parallel num_threads(%num_threads : i32) if(%if_cond) proc_bind(close) {
    omp.terminator
  }
 
@@ -764,17 +764,16 @@ func.func @omp_target(%if_cond : i1, %device : si32,  %num_threads : i32, %devic
     return
 }
 
-// CHECK-LABEL: omp_target_data
 func.func @omp_target_data (%if_cond : i1, %device : si32, %device_ptr: memref<i32>, %device_addr: memref<?xi32>, %map1: memref<?xi32>, %map2: memref<?xi32>) -> () {
     // CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[VAL_2:.*]] : memref<?xi32>, tensor<?xi32>)   map_clauses(always, from) capture(ByRef) -> memref<?xi32> {name = ""}
-    // CHECK: omp.target_data if(%[[VAL_0:.*]] : i1) device(%[[VAL_1:.*]] : si32) map_entries(%[[MAP_A]] : memref<?xi32>)
+    // CHECK: omp.target_data if(%[[VAL_0:.*]]) device(%[[VAL_1:.*]] : si32) map_entries(%[[MAP_A]] : memref<?xi32>)
     %mapv1 = omp.map.info var_ptr(%map1 : memref<?xi32>, tensor<?xi32>)   map_clauses(always, from) capture(ByRef) -> memref<?xi32> {name = ""}
-    omp.target_data if(%if_cond : i1) device(%device : si32) map_entries(%mapv1 : memref<?xi32>){}
+    omp.target_data if(%if_cond) device(%device : si32) map_entries(%mapv1 : memref<?xi32>){}
 
     // CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[VAL_2:.*]] : memref<?xi32>, tensor<?xi32>)   map_clauses(close, present, to) capture(ByRef) -> memref<?xi32> {name = ""}
-    // CHECK: omp.target_data map_entries(%[[MAP_A]] : memref<?xi32>) use_device_ptr(%[[VAL_3:.*]] : memref<i32>) use_device_addr(%[[VAL_4:.*]] : memref<?xi32>)
+    // CHECK: omp.target_data use_device_ptr(%[[VAL_3:.*]] : memref<i32>) use_device_addr(%[[VAL_4:.*]] : memref<?xi32>) map_entries(%[[MAP_A]] : memref<?xi32>)
     %mapv2 = omp.map.info var_ptr(%map1 : memref<?xi32>, tensor<?xi32>)   map_clauses(close, present, to) capture(ByRef) -> memref<?xi32> {name = ""}
-    omp.target_data map_entries(%mapv2 : memref<?xi32>) use_device_ptr(%device_ptr : memref<i32>) use_device_addr(%device_addr : memref<?xi32>) {}
+    omp.target_data use_device_ptr(%device_ptr : memref<i32>) use_device_addr(%device_addr : memref<?xi32>) map_entries(%mapv2 : memref<?xi32>) {}
 
     // CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[VAL_1:.*]] : memref<?xi32>, tensor<?xi32>)   map_clauses(tofrom) capture(ByRef) -> memref<?xi32> {name = ""}
     // CHECK: %[[MAP_B:.*]] = omp.map.info var_ptr(%[[VAL_2:.*]] : memref<?xi32>, tensor<?xi32>)   map_clauses(exit_release_or_enter_alloc) capture(ByRef) -> memref<?xi32> {name = ""}
@@ -784,14 +783,14 @@ func.func @omp_target_data (%if_cond : i1, %device : si32, %device_ptr: memref<i
     omp.target_data map_entries(%mapv3, %mapv4 : memref<?xi32>, memref<?xi32>) {}
 
     // CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[VAL_3:.*]] : memref<?xi32>, tensor<?xi32>)   map_clauses(exit_release_or_enter_alloc) capture(ByRef) -> memref<?xi32> {name = ""}
-    // CHECK: omp.target_enter_data if(%[[VAL_0:.*]] : i1) device(%[[VAL_1:.*]] : si32) nowait map_entries(%[[MAP_A]] : memref<?xi32>)
+    // CHECK: omp.target_enter_data if(%[[VAL_0:.*]]) device(%[[VAL_1:.*]] : si32) nowait map_entries(%[[MAP_A]] : memref<?xi32>)
     %mapv5 = omp.map.info var_ptr(%map1 : memref<?xi32>, tensor<?xi32>)   map_clauses(exit_release_or_enter_alloc) capture(ByRef) -> memref<?xi32> {name = ""}
-    omp.target_enter_data if(%if_cond : i1) device(%device : si32) nowait map_entries(%mapv5 : memref<?xi32>)
+    omp.target_enter_data if(%if_cond) device(%device : si32) nowait map_entries(%mapv5 : memref<?xi32>)
 
     // CHECK: %[[MAP_A:.*]] = omp.map.info var_ptr(%[[VAL_3:.*]] : memref<?xi32>, tensor<?xi32>)   map_clauses(exit_release_or_enter_alloc) capture(ByRef) -> memref<?xi32> {name = ""}
-    // CHECK: omp.target_exit_data if(%[[VAL_0:.*]] : i1) device(%[[VAL_1:.*]] : si32) nowait map_entries(%[[MAP_A]] : memref<?xi32>)
+    // CHECK: omp.target_exit_data if(%[[VAL_0:.*]]) device(%[[VAL_1:.*]] : si32) nowait map_entries(%[[MAP_A]] : memref<?xi32>)
     %mapv6 = omp.map.info var_ptr(%map2 : memref<?xi32>, tensor<?xi32>)   map_clauses(exit_release_or_enter_alloc) capture(ByRef) -> memref<?xi32> {name = ""}
-    omp.target_exit_data if(%if_cond : i1) device(%device : si32) nowait map_entries(%mapv6 : memref<?xi32>)
+    omp.target_exit_data if(%if_cond) device(%device : si32) nowait map_entries(%mapv6 : memref<?xi32>)
 
     return
 }
@@ -1719,7 +1718,7 @@ func.func @omp_atomic_capture(%v: memref<i32>, %x: memref<i32>, %expr: i32) {
     omp.atomic.read %v = %x : memref<i32>, i32
   }
 
-  // CHECK: omp.atomic.capture memory_order(seq_cst) hint(contended, speculative) {
+  // CHECK: omp.atomic.capture hint(contended, speculative) memory_order(seq_cst) {
   // CHECK-NEXT: omp.atomic.update %[[x]] : memref<i32>
   // CHECK-NEXT: (%[[xval:.*]]: i32):
   // CHECK-NEXT:   %[[newval:.*]] = llvm.add %[[xval]], %[[expr]] : i32
@@ -1943,8 +1942,8 @@ func.func @omp_task(%bool_var: i1, %i64_var: i64, %i32_var: i32, %data_var: memr
   }
 
   // Checking priority clause
-  // CHECK: omp.task priority(%[[i32_var]]) {
-  omp.task priority(%i32_var) {
+  // CHECK: omp.task priority(%[[i32_var]] : i32) {
+  omp.task priority(%i32_var : i32) {
     // CHECK: "test.foo"() : () -> ()
     "test.foo"() : () -> ()
     // CHECK: omp.terminator
@@ -1965,8 +1964,8 @@ func.func @omp_task(%bool_var: i1, %i64_var: i64, %i32_var: i32, %data_var: memr
   omp.task if(%bool_var) final(%bool_var) untied
       // CHECK-SAME: in_reduction(@add_f32 -> %[[redn_var1]] : !llvm.ptr, @add_f32 -> %[[redn_var2]] : !llvm.ptr)
       in_reduction(@add_f32 -> %0 : !llvm.ptr, @add_f32 -> %1 : !llvm.ptr)
-      // CHECK-SAME: priority(%[[i32_var]])
-      priority(%i32_var)
+      // CHECK-SAME: priority(%[[i32_var]] : i32)
+      priority(%i32_var : i32)
       // CHECK-SAME: allocate(%[[data_var]] : memref<i32> -> %[[data_var]] : memref<i32>)
       allocate(%data_var : memref<i32> -> %data_var : memref<i32>) {
     // CHECK: "test.foo"() : () -> ()
@@ -2237,7 +2236,7 @@ func.func @omp_taskloop(%lb: i32, %ub: i32, %step: i32) -> () {
 
   %testi32 = "test.i32"() : () -> (i32)
   // CHECK: omp.taskloop priority(%{{[^:]+}}: i32) {
-  omp.taskloop priority(%testi32: i32) {
+  omp.taskloop priority(%testi32 : i32) {
     omp.loop_nest (%i, %j) : i32 = (%lb, %ub) to (%ub, %lb) step (%step, %step) {
       // CHECK: omp.yield
       omp.yield
@@ -2407,8 +2406,8 @@ func.func @omp_target_update_data (%if_cond : i1, %device : si32, %map1: memref<
 
     %mapv_to = omp.map.info var_ptr(%map2 : memref<?xi32>, tensor<?xi32>) map_clauses(present, to) capture(ByRef) -> memref<?xi32> {name = ""}
 
-    // CHECK: omp.target_update if(%[[VAL_0:.*]] : i1) device(%[[VAL_1:.*]] : si32) nowait motion_entries(%{{.*}}, %{{.*}} : memref<?xi32>, memref<?xi32>)
-    omp.target_update if(%if_cond : i1) device(%device : si32) nowait motion_entries(%mapv_from , %mapv_to : memref<?xi32>, memref<?xi32>)
+    // CHECK: omp.target_update if(%[[VAL_0:.*]]) device(%[[VAL_1:.*]] : si32) nowait map_entries(%{{.*}}, %{{.*}} : memref<?xi32>, memref<?xi32>)
+    omp.target_update if(%if_cond) device(%device : si32) nowait map_entries(%mapv_from , %mapv_to : memref<?xi32>, memref<?xi32>)
     return
 }
 
@@ -2444,8 +2443,8 @@ func.func @omp_target_enter_update_exit_data_depend(%a: memref<?xi32>, %b: memre
   }
 
   // Then map that over to the target
-  // CHECK: omp.target_enter_data nowait map_entries([[MAP0]], [[MAP2]] : memref<?xi32>, memref<?xi32>) depend(taskdependin -> [[ARG0]] : memref<?xi32>)
-  omp.target_enter_data nowait map_entries(%map_a, %map_c: memref<?xi32>, memref<?xi32>) depend(taskdependin ->  %a: memref<?xi32>)
+  // CHECK: omp.target_enter_data depend(taskdependin -> [[ARG0]] : memref<?xi32>) nowait map_entries([[MAP0]], [[MAP2]] : memref<?xi32>, memref<?xi32>)
+  omp.target_enter_data depend(taskdependin ->  %a: memref<?xi32>) nowait map_entries(%map_a, %map_c: memref<?xi32>, memref<?xi32>)
 
   // Compute 'b' on the target and copy it back
   // CHECK: omp.target map_entries([[MAP1]] -> {{%.*}} : memref<?xi32>) {
@@ -2461,8 +2460,8 @@ func.func @omp_target_enter_update_exit_data_depend(%a: memref<?xi32>, %b: memre
   }
 
   // Copy the updated 'a' onto the target
-  // CHECK: omp.target_update nowait motion_entries([[MAP0]] : memref<?xi32>) depend(taskdependin -> [[ARG0]] : memref<?xi32>)
-  omp.target_update motion_entries(%map_a :  memref<?xi32>) depend(taskdependin -> %a : memref<?xi32>) nowait
+  // CHECK: omp.target_update depend(taskdependin -> [[ARG0]] : memref<?xi32>) nowait map_entries([[MAP0]] : memref<?xi32>)
+  omp.target_update depend(taskdependin -> %a : memref<?xi32>) nowait map_entries(%map_a :  memref<?xi32>)
 
   // Compute 'c' on the target and copy it back
   %map_c_from = omp.map.info var_ptr(%c: memref<?xi32>, tensor<?xi32>) map_clauses(from) capture(ByRef) -> memref<?xi32>
@@ -2471,8 +2470,8 @@ func.func @omp_target_enter_update_exit_data_depend(%a: memref<?xi32>, %b: memre
     "test.foobar"() : ()->()
     omp.terminator
   }
-  // CHECK: omp.target_exit_data map_entries([[MAP2]] : memref<?xi32>) depend(taskdependin -> [[ARG2]] : memref<?xi32>)
-  omp.target_exit_data map_entries(%map_c : memref<?xi32>) depend(taskdependin -> %c : memref<?xi32>)
+  // CHECK: omp.target_exit_data depend(taskdependin -> [[ARG2]] : memref<?xi32>) map_entries([[MAP2]] : memref<?xi32>)
+  omp.target_exit_data depend(taskdependin -> %c : memref<?xi32>) map_entries(%map_c : memref<?xi32>)
   return
 }
 
diff --git a/mlir/test/Target/LLVMIR/omptarget-llvm.mlir b/mlir/test/Target/LLVMIR/omptarget-llvm.mlir
index 2f629675442d0..94386a1427486 100644
--- a/mlir/test/Target/LLVMIR/omptarget-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/omptarget-llvm.mlir
@@ -110,7 +110,7 @@ llvm.func @_QPomp_target_enter_exit(%1 : !llvm.ptr, %3 : !llvm.ptr) {
   %22 = llvm.mlir.constant(1 : index) : i64
   %23 = omp.map.bounds   lower_bound(%20 : i64) upper_bound(%19 : i64) extent(%21 : i64) stride(%22 : i64) start_idx(%22 : i64)
   %map2 = omp.map.info var_ptr(%3 : !llvm.ptr, !llvm.array<512 x i32>)   map_clauses(exit_release_or_enter_alloc) capture(ByRef) bounds(%23) -> !llvm.ptr {name = ""}
-  omp.target_enter_data   if(%12 : i1) device(%13 : i32) map_entries(%map1, %map2 : !llvm.ptr, !llvm.ptr)
+  omp.target_enter_data   if(%12) device(%13 : i32) map_entries(%map1, %map2 : !llvm.ptr, !llvm.ptr)
   %24 = llvm.load %7 : !llvm.ptr -> i32
   %25 = llvm.mlir.constant(10 : i32) : i32
   %26 = llvm.icmp "sgt" %24, %25 : i32
@@ -127,7 +127,7 @@ llvm.func @_QPomp_target_enter_exit(%1 : !llvm.ptr, %3 : !llvm.ptr) {
   %36 = llvm.mlir.constant(1 : index) : i64
   %37 = omp.map.bounds   lower_bound(%34 : i64) upper_bound(%33 : i64) extent(%35 : i64) stride(%36 : i64) start_idx(%36 : i64)
   %map4 = omp.map.info var_ptr(%3 : !llvm.ptr, !llvm.array<512 x i32>)   map_clauses(exit_release_or_enter_alloc) capture(ByRef) bounds(%37) -> !llvm.ptr {name = ""}
-  omp.target_exit_data   if(%26 : i1) device(%27 : i32) map_entries(%map3, %map4 : !llvm.ptr, !llvm.ptr)
+  omp.target_exit_data   if(%26) device(%27 : i32) map_entries(%map3, %map4 : !llvm.ptr, !llvm.ptr)
   llvm.return
 }
 
@@ -457,7 +457,7 @@ llvm.func @_QPopenmp_target_data_update() {
     omp.terminator
   }
 
-  omp.target_update motion_entries(%2 : !llvm.ptr)
+  omp.target_update map_entries(%2 : !llvm.ptr)
 
   llvm.return
 }
diff --git a/mlir/test/Target/LLVMIR/omptarget-nowait-llvm.mlir b/mlir/test/Target/LLVMIR/omptarget-nowait-llvm.mlir
index f0d88f5c13536..1e2fbe86d13c4 100644
--- a/mlir/test/Target/LLVMIR/omptarget-nowait-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/omptarget-nowait-llvm.mlir
@@ -6,7 +6,7 @@ llvm.func @_QPopenmp_target_data_update() {
   %2 = omp.map.info var_ptr(%1 : !llvm.ptr, i32)   map_clauses(to) capture(ByRef) -> !llvm.ptr {name = ""}
 
   // CHECK: error: `nowait` is not supported yet
-  omp.target_update motion_entries(%2 : !llvm.ptr) nowait
+  omp.target_update map_entries(%2 : !llvm.ptr) nowait
 
   llvm.return
 }
diff --git a/mlir/test/Target/LLVMIR/omptarget-parallel-llvm.mlir b/mlir/test/Target/LLVMIR/omptarget-parallel-llvm.mlir
index f22bc12d6b94c..a714e594d7812 100644
--- a/mlir/test/Target/LLVMIR/omptarget-parallel-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/omptarget-parallel-llvm.mlir
@@ -44,7 +44,7 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<"dlti.alloca_memo
       %5 = llvm.load %arg2 : !llvm.ptr -> i32
       %6 = llvm.mlir.constant(0 : i64) : i32
       %7 = llvm.icmp "ne" %5, %6 : i32
-      omp.parallel if(%7 : i1) {
+      omp.parallel if(%7) {
         llvm.store %4, %arg1 : i32, !llvm.ptr
         omp.terminator
       }
@@ -94,7 +94,7 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<"dlti.alloca_memo
 //
 // This test checks if MLIR expression:
 //      %7 = llvm.icmp "ne" %5, %6 : i32
-//      omp.parallel if(%7 : i1)
+//      omp.parallel if(%7)
 // is correctly lowered to LLVM IR code and the if condition variable
 // is passed as a param to kmpc_parallel_51 function
 
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
index ad40ca26bec9f..d9946619fce55 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -164,7 +164,7 @@ llvm.func @test_omp_parallel_if_1(%arg0: i32) -> () {
 // CHECK: br label %[[OUTLINED_EXIT_IF_1:.*]]
 // CHECK: [[OUTLINED_EXIT_IF_1]]:
 // CHECK: br label %[[RETURN_BLOCK_IF_1:.*]]
-  omp.parallel if(%1 : i1) {
+  omp.parallel if(%1) {
     omp.barrier
     omp.terminator
   }



More information about the llvm-branch-commits mailing list