xref: /llvm-project/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td (revision afcbcae668f1d8061974247f2828190173aef742)
1//===-- OpenMPOps.td - OpenMP dialect operation definitions *- tablegen -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines the basic operations for the OpenMP dialect.
10//
11//===----------------------------------------------------------------------===//
12
13
14#ifndef OPENMP_OPS
15#define OPENMP_OPS
16
17include "mlir/Dialect/LLVMIR/LLVMOpBase.td"
18include "mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td"
19include "mlir/Dialect/OpenACCMPCommon/Interfaces/OpenACCMPOpsInterfaces.td"
20include "mlir/Dialect/OpenMP/OpenMPClauses.td"
21include "mlir/Dialect/OpenMP/OpenMPOpBase.td"
22include "mlir/Interfaces/ControlFlowInterfaces.td"
23include "mlir/Interfaces/SideEffectInterfaces.td"
24include "mlir/IR/EnumAttr.td"
25include "mlir/IR/OpBase.td"
26include "mlir/IR/SymbolInterfaces.td"
27
28//===----------------------------------------------------------------------===//
29// 2.19.4 Data-Sharing Attribute Clauses
30//===----------------------------------------------------------------------===//
31
32def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove, RecipeInterface]> {
33  let summary = "Provides declaration of [first]private logic.";
34  let description = [{
35    This operation provides a declaration of how to implement the
36    [first]privatization of a variable. The dialect users should provide
37    information about how to create an instance of the type in the alloc region,
38    how to initialize the copy from the original item in the copy region, and if
39    needed, how to deallocate allocated memory in the dealloc region.
40
41    Examples:
42
43    * `private(x)` would be emitted as:
44    ```mlir
45    omp.private {type = private} @x.privatizer : !fir.ref<i32> alloc {
46    ^bb0(%arg0: !fir.ref<i32>):
47    %0 = ... allocate proper memory for the private clone ...
48    omp.yield(%0 : !fir.ref<i32>)
49    }
50    ```
51
52    * `firstprivate(x)` would be emitted as:
53    ```mlir
54    omp.private {type = firstprivate} @x.privatizer : !fir.ref<i32> alloc {
55    ^bb0(%arg0: !fir.ref<i32>):
56    %0 = ... allocate proper memory for the private clone ...
57    omp.yield(%0 : !fir.ref<i32>)
58    } copy {
59    ^bb0(%arg0: !fir.ref<i32>, %arg1: !fir.ref<i32>):
60    // %arg0 is the original host variable. Same as for `alloc`.
61    // %arg1 represents the memory allocated in `alloc`.
62    ... copy from host to the privatized clone ....
63    omp.yield(%arg1 : !fir.ref<i32>)
64    }
65    ```
66
67    * `private(x)` for "allocatables" would be emitted as:
68    ```mlir
69    omp.private {type = private} @x.privatizer : !some.type alloc {
70    ^bb0(%arg0: !some.type):
71    %0 = ... allocate proper memory for the private clone ...
72    omp.yield(%0 : !fir.ref<i32>)
73    } dealloc {
74    ^bb0(%arg0: !some.type):
75    ... deallocate allocated memory ...
76    omp.yield
77    }
78    ```
79
80    There are no restrictions on the body except for:
81    - The `alloc` & `dealloc` regions have a single argument.
82    - The `copy` region has 2 arguments.
83    - All three regions are terminated by `omp.yield` ops.
84    The above restrictions and other obvious restrictions (e.g. verifying the
85    type of yielded values) are verified by the custom op verifier. The actual
86    contents of the blocks inside all regions are not verified.
87
88    Instances of this op would then be used by ops that model directives that
89    accept data-sharing attribute clauses.
90
91    The $sym_name attribute provides a symbol by which the privatizer op can be
92    referenced by other dialect ops.
93
94    The $type attribute is the type of the value being privatized.
95
96    The $data_sharing_type attribute specifies whether privatizer corresponds
97    to a `private` or a `firstprivate` clause.
98  }];
99
100  let arguments = (ins SymbolNameAttr:$sym_name,
101                       TypeAttrOf<AnyType>:$type,
102                       DataSharingClauseTypeAttr:$data_sharing_type);
103
104  let regions = (region MinSizedRegion<1>:$alloc_region,
105                        AnyRegion:$copy_region,
106                        AnyRegion:$dealloc_region);
107
108  let assemblyFormat = [{
109    $data_sharing_type $sym_name `:` $type
110      `alloc` $alloc_region
111      (`copy` $copy_region^)?
112      (`dealloc` $dealloc_region^)?
113      attr-dict
114  }];
115
116  let builders = [
117    OpBuilder<(ins CArg<"TypeRange">:$result,
118                   CArg<"StringAttr">:$sym_name,
119                   CArg<"TypeAttr">:$type)>
120  ];
121
122  let extraClassDeclaration = [{
123    BlockArgument getAllocMoldArg() {
124      return getAllocRegion().getArgument(0);
125    }
126    BlockArgument getCopyMoldArg() {
127      auto &region = getCopyRegion();
128      return region.empty() ? nullptr : region.getArgument(0);
129    }
130    BlockArgument getCopyPrivateArg() {
131      auto &region = getCopyRegion();
132      return region.empty() ? nullptr : region.getArgument(1);
133    }
134    BlockArgument getDeallocMoldArg() {
135      auto &region = getDeallocRegion();
136      return region.empty() ? nullptr : region.getArgument(0);
137    }
138
139    /// needsMap returns true if the value being privatized should additionally
140    /// be mapped to the target region using a MapInfoOp. This is most common
141    /// when an allocatable is privatized. In such cases, the descriptor is used
142    /// in privatization and needs to be mapped on to the device.
143    bool needsMap() {
144      return !getAllocMoldArg().use_empty();
145    }
146  }];
147
148  let hasRegionVerifier = 1;
149}
150
151//===----------------------------------------------------------------------===//
152// 2.6 parallel Construct
153//===----------------------------------------------------------------------===//
154
155def ParallelOp : OpenMP_Op<"parallel", traits = [
156    AttrSizedOperandSegments, AutomaticAllocationScope,
157    DeclareOpInterfaceMethods<ComposableOpInterface>,
158    DeclareOpInterfaceMethods<OutlineableOpenMPOpInterface>,
159    RecursiveMemoryEffects
160  ], clauses = [
161    OpenMP_AllocateClause, OpenMP_IfClause, OpenMP_NumThreadsClause,
162    OpenMP_PrivateClause, OpenMP_ProcBindClause, OpenMP_ReductionClause
163  ], singleRegion = true> {
164  let summary = "parallel construct";
165  let description = [{
166    The parallel construct includes a region of code which is to be executed
167    by a team of threads.
168
169    The optional `if_expr` parameter specifies a boolean result of a conditional
170    check. If this value is 1 or is not provided then the parallel region runs
171    as normal, if it is 0 then the parallel region is executed with one thread.
172  }] # clausesDescription;
173
174  let builders = [
175    OpBuilder<(ins CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>,
176    OpBuilder<(ins CArg<"const ParallelOperands &">:$clauses)>
177  ];
178
179  let assemblyFormat = clausesAssemblyFormat # [{
180    custom<PrivateReductionRegion>($region, $private_vars, type($private_vars),
181        $private_syms, $reduction_mod, $reduction_vars, type($reduction_vars), $reduction_byref,
182        $reduction_syms) attr-dict
183  }];
184
185  let hasVerifier = 1;
186  let hasRegionVerifier = 1;
187}
188
189def TerminatorOp : OpenMP_Op<"terminator", [Terminator, Pure]> {
190  let summary = "terminator for OpenMP regions";
191  let description = [{
192    A terminator operation for regions that appear in the body of OpenMP
193    operation.  These regions are not expected to return any value so the
194    terminator takes no operands. The terminator op returns control to the
195    enclosing op.
196  }];
197
198  let assemblyFormat = "attr-dict";
199}
200
201//===----------------------------------------------------------------------===//
202// 2.7 teams Construct
203//===----------------------------------------------------------------------===//
204def TeamsOp : OpenMP_Op<"teams", traits = [
205    AttrSizedOperandSegments, RecursiveMemoryEffects
206  ], clauses = [
207    OpenMP_AllocateClause, OpenMP_IfClause, OpenMP_NumTeamsClause,
208    OpenMP_PrivateClause, OpenMP_ReductionClause, OpenMP_ThreadLimitClause
209  ], singleRegion = true> {
210  let summary = "teams construct";
211  let description = [{
212    The teams construct defines a region of code that triggers the creation of a
213    league of teams. Once created, the number of teams remains constant for the
214    duration of its code region.
215
216    If the `if_expr` is present and it evaluates to `false`, the number of teams
217    created is one.
218  }] # clausesDescription;
219
220  let builders = [
221    OpBuilder<(ins CArg<"const TeamsOperands &">:$clauses)>
222  ];
223
224  let assemblyFormat = clausesAssemblyFormat # [{
225    custom<PrivateReductionRegion>($region, $private_vars, type($private_vars),
226        $private_syms, $reduction_mod, $reduction_vars, type($reduction_vars), $reduction_byref,
227        $reduction_syms) attr-dict
228  }];
229
230  let hasVerifier = 1;
231}
232
233//===----------------------------------------------------------------------===//
234// 2.8.1 Sections Construct
235//===----------------------------------------------------------------------===//
236
237def SectionOp : OpenMP_Op<"section", traits = [
238    BlockArgOpenMPOpInterface, HasParent<"SectionsOp">
239  ], singleRegion = true> {
240  let summary = "section directive";
241  let description = [{
242    A section operation encloses a region which represents one section in a
243    sections construct. A section op should always be surrounded by an
244    `omp.sections` operation. The section operation may have block args
245    which corespond to the block arguments of the surrounding `omp.sections`
246    operation. This is done to reflect situations where these block arguments
247    represent variables private to each section.
248  }];
249  let extraClassDeclaration = [{
250    // Override BlockArgOpenMPOpInterface methods based on the parent
251    // omp.sections operation. Only forward-declare here because SectionsOp is
252    // not completely defined at this point.
253    unsigned numPrivateBlockArgs();
254    unsigned numReductionBlockArgs();
255  }] # clausesExtraClassDeclaration;
256  let assemblyFormat = "$region attr-dict";
257}
258
259def SectionsOp : OpenMP_Op<"sections", traits = [
260    AttrSizedOperandSegments
261  ], clauses = [
262    OpenMP_AllocateClause, OpenMP_NowaitClause, OpenMP_PrivateClause,
263    OpenMP_ReductionClause
264  ], singleRegion = true> {
265  let summary = "sections construct";
266  let description = [{
267    The sections construct is a non-iterative worksharing construct that
268    contains `omp.section` operations. The `omp.section` operations are to be
269    distributed among and executed by the threads in a team. Each `omp.section`
270    is executed once by one of the threads in the team in the context of its
271    implicit task.
272    Block arguments for reduction variables should be mirrored in enclosed
273    `omp.section` operations.
274  }] # clausesDescription;
275
276  // Override region definition.
277  let regions = (region SizedRegion<1>:$region);
278
279  let builders = [
280    OpBuilder<(ins CArg<"const SectionsOperands &">:$clauses)>
281  ];
282
283  let assemblyFormat = clausesAssemblyFormat # [{
284    custom<PrivateReductionRegion>($region, $private_vars, type($private_vars),
285        $private_syms, $reduction_mod, $reduction_vars, type($reduction_vars), $reduction_byref,
286        $reduction_syms) attr-dict
287  }];
288
289  let hasVerifier = 1;
290  let hasRegionVerifier = 1;
291}
292
293//===----------------------------------------------------------------------===//
294// 2.8.2 Single Construct
295//===----------------------------------------------------------------------===//
296
297def SingleOp : OpenMP_Op<"single", traits = [
298    AttrSizedOperandSegments
299  ], clauses = [
300    OpenMP_AllocateClause, OpenMP_CopyprivateClause, OpenMP_NowaitClause,
301    OpenMP_PrivateClause
302  ], singleRegion = true> {
303  let summary = "single directive";
304  let description = [{
305    The single construct specifies that the associated structured block is
306    executed by only one of the threads in the team (not necessarily the
307    master thread), in the context of its implicit task. The other threads
308    in the team, which do not execute the block, wait at an implicit barrier
309    at the end of the single construct.
310  }] # clausesDescription;
311
312  let builders = [
313    OpBuilder<(ins CArg<"const SingleOperands &">:$clauses)>
314  ];
315
316  let assemblyFormat = clausesAssemblyFormat # [{
317    custom<PrivateRegion>($region, $private_vars, type($private_vars),
318        $private_syms) attr-dict
319  }];
320
321  let hasVerifier = 1;
322}
323
324//===----------------------------------------------------------------------===//
325// 2.8.3 Workshare Construct
326//===----------------------------------------------------------------------===//
327
328def WorkshareOp : OpenMP_Op<"workshare", traits = [
329    RecursiveMemoryEffects,
330  ], clauses = [
331    OpenMP_NowaitClause,
332  ], singleRegion = true> {
333  let summary = "workshare directive";
334  let description = [{
335    The workshare construct divides the execution of the enclosed structured
336    block into separate units of work, and causes the threads of the team to
337    share the work such that each unit is executed only once by one thread, in
338    the context of its implicit task
339
340    This operation is used for the intermediate representation of the workshare
341    block before the work gets divided between the threads. See the flang
342    LowerWorkshare pass for details.
343  }] # clausesDescription;
344
345  let builders = [
346    OpBuilder<(ins CArg<"const WorkshareOperands &">:$clauses)>
347  ];
348}
349
350def WorkshareLoopWrapperOp : OpenMP_Op<"workshare.loop_wrapper", traits = [
351    DeclareOpInterfaceMethods<LoopWrapperInterface>, NoTerminator,
352    RecursiveMemoryEffects, SingleBlock
353  ], singleRegion = true> {
354  let summary = "contains loop nests to be parallelized by workshare";
355  let description = [{
356    This operation wraps a loop nest that is marked for dividing into units of
357    work by an encompassing omp.workshare operation.
358  }];
359
360  let builders = [
361    OpBuilder<(ins), [{ build($_builder, $_state, {}); }]>
362  ];
363  let assemblyFormat = "$region attr-dict";
364  let hasVerifier = 1;
365}
366
367//===----------------------------------------------------------------------===//
368// Loop Nest
369//===----------------------------------------------------------------------===//
370
371def LoopNestOp : OpenMP_Op<"loop_nest", traits = [
372    RecursiveMemoryEffects, SameVariadicOperandSize
373  ], clauses = [
374    OpenMP_LoopRelatedClause
375  ], singleRegion = true> {
376  let summary = "rectangular loop nest";
377  let description = [{
378    This operation represents a collapsed rectangular loop nest. For each
379    rectangular loop of the nest represented by an instance of this operation,
380    lower and upper bounds, as well as a step variable, must be defined.
381
382    The lower and upper bounds specify a half-open range: the range includes the
383    lower bound but does not include the upper bound. If the `loop_inclusive`
384    attribute is specified then the upper bound is also included.
385
386    The body region can contain any number of blocks. The region is terminated
387    by an `omp.yield` instruction without operands. The induction variables,
388    represented as entry block arguments to the loop nest operation's single
389    region, match the types of the `loop_lower_bounds`, `loop_upper_bounds` and
390    `loop_steps` arguments.
391
392    ```mlir
393    omp.loop_nest (%i1, %i2) : i32 = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
394      %a = load %arrA[%i1, %i2] : memref<?x?xf32>
395      %b = load %arrB[%i1, %i2] : memref<?x?xf32>
396      %sum = arith.addf %a, %b : f32
397      store %sum, %arrC[%i1, %i2] : memref<?x?xf32>
398      omp.yield
399    }
400    ```
401
402    This is a temporary simplified definition of a loop based on existing OpenMP
403    loop operations intended to serve as a stopgap solution until the long-term
404    representation of canonical loops is defined. Specifically, this operation
405    is intended to serve as a unique source for loop information during the
406    transition to making `omp.distribute`, `omp.simd`, `omp.taskloop` and
407    `omp.wsloop` wrapper operations. It is not intended to help with the
408    addition of support for loop transformations, non-rectangular loops and
409    non-perfectly nested loops.
410  }];
411
412  let builders = [
413    OpBuilder<(ins CArg<"const LoopNestOperands &">:$clauses)>
414  ];
415
416  let extraClassDeclaration = [{
417    /// Returns the induction variables of the loop nest.
418    ArrayRef<BlockArgument> getIVs() { return getRegion().getArguments(); }
419
420    /// Fills a list of wrapper operations around this loop nest. Wrappers
421    /// in the resulting vector will be sorted from innermost to outermost.
422    void gatherWrappers(SmallVectorImpl<LoopWrapperInterface> &wrappers);
423  }] # clausesExtraClassDeclaration;
424
425  // Disable inherited clause-based declarative assembly format and instead
426  // enable using the custom parser-printer implemented in C++.
427  let assemblyFormat = ?;
428  let hasCustomAssemblyFormat = 1;
429  let hasVerifier = 1;
430}
431
432//===----------------------------------------------------------------------===//
433// 2.9.2 Workshare Loop Construct
434//===----------------------------------------------------------------------===//
435
436def LoopOp : OpenMP_Op<"loop", traits = [
437    AttrSizedOperandSegments, DeclareOpInterfaceMethods<LoopWrapperInterface>,
438    NoTerminator, SingleBlock
439  ], clauses = [
440    OpenMP_BindClause, OpenMP_PrivateClause, OpenMP_OrderClause,
441    OpenMP_ReductionClause
442  ], singleRegion = true> {
443  let summary = "loop construct";
444  let description = [{
445    A loop construct specifies that the logical iterations of the associated loops
446    may execute concurrently and permits the encountering threads to execute the
447    loop accordingly. A loop construct can have 3 different types of binding:
448      1. teams: in which case the binding region is the innermost enclosing `teams`
449         region.
450      2. parallel: in which case the binding region is the innermost enclosing `parallel`
451         region.
452      3. thread: in which case the binding region is not defined.
453
454    The body region can only contain a single block which must contain a single
455    operation, this operation must be an `omp.loop_nest`.
456
457    ```
458    omp.loop <clauses> {
459      omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
460        %a = load %arrA[%i1, %i2] : memref<?x?xf32>
461        %b = load %arrB[%i1, %i2] : memref<?x?xf32>
462        %sum = arith.addf %a, %b : f32
463        store %sum, %arrC[%i1, %i2] : memref<?x?xf32>
464        omp.yield
465      }
466    }
467    ```
468  }] # clausesDescription;
469
470  let assemblyFormat = clausesAssemblyFormat # [{
471    custom<PrivateReductionRegion>($region, $private_vars, type($private_vars),
472        $private_syms, $reduction_mod, $reduction_vars, type($reduction_vars), $reduction_byref,
473        $reduction_syms) attr-dict
474  }];
475
476  let builders = [
477    OpBuilder<(ins CArg<"const LoopOperands &">:$clauses)>
478  ];
479
480  let hasVerifier = 1;
481  let hasRegionVerifier = 1;
482}
483
484def WsloopOp : OpenMP_Op<"wsloop", traits = [
485    AttrSizedOperandSegments, DeclareOpInterfaceMethods<ComposableOpInterface>,
486    DeclareOpInterfaceMethods<LoopWrapperInterface>, NoTerminator,
487    RecursiveMemoryEffects, SingleBlock
488  ], clauses = [
489    OpenMP_AllocateClause, OpenMP_LinearClause, OpenMP_NowaitClause,
490    OpenMP_OrderClause, OpenMP_OrderedClause, OpenMP_PrivateClause,
491    OpenMP_ReductionClause, OpenMP_ScheduleClause
492  ], singleRegion = true> {
493  let summary = "worksharing-loop construct";
494  let description = [{
495    The worksharing-loop construct specifies that the iterations of the loop(s)
496    will be executed in parallel by threads in the current context. These
497    iterations are spread across threads that already exist in the enclosing
498    parallel region.
499
500    The body region can only contain a single block which must contain a single
501    operation. This operation must be another compatible loop wrapper or an
502    `omp.loop_nest`.
503
504    ```
505    omp.wsloop <clauses> {
506      omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
507        %a = load %arrA[%i1, %i2] : memref<?x?xf32>
508        %b = load %arrB[%i1, %i2] : memref<?x?xf32>
509        %sum = arith.addf %a, %b : f32
510        store %sum, %arrC[%i1, %i2] : memref<?x?xf32>
511        omp.yield
512      }
513    }
514    ```
515  }] # clausesDescription;
516
517  let builders = [
518    OpBuilder<(ins CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>,
519    OpBuilder<(ins CArg<"const WsloopOperands &">:$clauses)>
520  ];
521
522  let assemblyFormat = clausesAssemblyFormat # [{
523    custom<PrivateReductionRegion>($region, $private_vars, type($private_vars),
524        $private_syms, $reduction_mod, $reduction_vars, type($reduction_vars), $reduction_byref,
525        $reduction_syms) attr-dict
526  }];
527
528  let hasVerifier = 1;
529  let hasRegionVerifier = 1;
530}
531
532//===----------------------------------------------------------------------===//
533// Simd construct [2.9.3.1]
534//===----------------------------------------------------------------------===//
535
536def SimdOp : OpenMP_Op<"simd", traits = [
537    AttrSizedOperandSegments, DeclareOpInterfaceMethods<ComposableOpInterface>,
538    DeclareOpInterfaceMethods<LoopWrapperInterface>, NoTerminator,
539    RecursiveMemoryEffects, SingleBlock
540  ], clauses = [
541    OpenMP_AlignedClause, OpenMP_IfClause, OpenMP_LinearClause,
542    OpenMP_NontemporalClause, OpenMP_OrderClause, OpenMP_PrivateClause,
543    OpenMP_ReductionClause, OpenMP_SafelenClause, OpenMP_SimdlenClause
544  ], singleRegion = true> {
545  let summary = "simd construct";
546  let description = [{
547    The simd construct can be applied to a loop to indicate that the loop can be
548    transformed into a SIMD loop (that is, multiple iterations of the loop can
549    be executed concurrently using SIMD instructions).
550
551    The body region can only contain a single block which must contain a single
552    operation. This operation must be another compatible loop wrapper or an
553    `omp.loop_nest`.
554
555    ```
556    omp.simd <clauses> {
557      omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
558        %a = load %arrA[%i1, %i2] : memref<?x?xf32>
559        %b = load %arrB[%i1, %i2] : memref<?x?xf32>
560        %sum = arith.addf %a, %b : f32
561        store %sum, %arrC[%i1, %i2] : memref<?x?xf32>
562        omp.yield
563      }
564    }
565    ```
566
567    When an if clause is present and evaluates to false, the preferred number of
568    iterations to be executed concurrently is one, regardless of whether
569    a simdlen clause is specified.
570  }] # clausesDescription;
571
572  let builders = [
573    OpBuilder<(ins CArg<"const SimdOperands &">:$clauses)>
574  ];
575
576  let assemblyFormat = clausesAssemblyFormat # [{
577    custom<PrivateReductionRegion>($region, $private_vars, type($private_vars),
578        $private_syms, $reduction_mod, $reduction_vars, type($reduction_vars), $reduction_byref,
579        $reduction_syms) attr-dict
580  }];
581
582  let hasVerifier = 1;
583  let hasRegionVerifier = 1;
584}
585
586
587def YieldOp : OpenMP_Op<"yield",
588    [Pure, ReturnLike, Terminator,
589     ParentOneOf<["AtomicUpdateOp", "DeclareReductionOp", "LoopNestOp",
590                  "PrivateClauseOp"]>]> {
591  let summary = "loop yield and termination operation";
592  let description = [{
593    "omp.yield" yields SSA values from the OpenMP dialect op region and
594    terminates the region. The semantics of how the values are yielded is
595    defined by the parent operation.
596  }];
597
598  let arguments = (ins Variadic<AnyType>:$results);
599
600  let builders = [
601    OpBuilder<(ins), [{ build($_builder, $_state, {}); }]>
602  ];
603
604  let assemblyFormat = "( `(` $results^ `:` type($results) `)` )? attr-dict";
605}
606
607//===----------------------------------------------------------------------===//
608// Distribute construct [2.9.4.1]
609//===----------------------------------------------------------------------===//
610def DistributeOp : OpenMP_Op<"distribute", traits = [
611    AttrSizedOperandSegments, DeclareOpInterfaceMethods<ComposableOpInterface>,
612    DeclareOpInterfaceMethods<LoopWrapperInterface>, NoTerminator,
613    RecursiveMemoryEffects, SingleBlock
614  ], clauses = [
615    OpenMP_AllocateClause, OpenMP_DistScheduleClause, OpenMP_OrderClause,
616    OpenMP_PrivateClause
617  ], singleRegion = true> {
618  let summary = "distribute construct";
619  let description = [{
620    The distribute construct specifies that the iterations of one or more loops
621    (optionally specified using collapse clause) will be executed by the
622    initial teams in the context of their implicit tasks. The loops that the
623    distribute op is associated with starts with the outermost loop enclosed by
624    the distribute op region and going down the loop nest toward the innermost
625    loop. The iterations are distributed across the initial threads of all
626    initial teams that execute the teams region to which the distribute region
627    binds.
628
629    The distribute loop construct specifies that the iterations of the loop(s)
630    will be executed in parallel by threads in the current context. These
631    iterations are spread across threads that already exist in the enclosing
632    region.
633
634    The body region can only contain a single block which must contain a single
635    operation. This operation must be another compatible loop wrapper or an
636    `omp.loop_nest`.
637
638    ```mlir
639    omp.distribute <clauses> {
640      omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
641        %a = load %arrA[%i1, %i2] : memref<?x?xf32>
642        %b = load %arrB[%i1, %i2] : memref<?x?xf32>
643        %sum = arith.addf %a, %b : f32
644        store %sum, %arrC[%i1, %i2] : memref<?x?xf32>
645        omp.yield
646      }
647    }
648    ```
649  }] # clausesDescription;
650
651  let builders = [
652    OpBuilder<(ins CArg<"const DistributeOperands &">:$clauses)>
653  ];
654
655  let assemblyFormat = clausesAssemblyFormat # [{
656    custom<PrivateRegion>($region, $private_vars, type($private_vars),
657        $private_syms) attr-dict
658  }];
659
660  let hasVerifier = 1;
661  let hasRegionVerifier = 1;
662}
663
664//===----------------------------------------------------------------------===//
665// 2.10.1 task Construct
666//===----------------------------------------------------------------------===//
667
668def TaskOp
669    : OpenMP_Op<"task",
670                traits = [AttrSizedOperandSegments, AutomaticAllocationScope,
671                          OutlineableOpenMPOpInterface],
672                clauses = [
673                    // TODO: Complete clause list (affinity, detach).
674                    OpenMP_AllocateClause, OpenMP_DependClause,
675                    OpenMP_FinalClause, OpenMP_IfClause,
676                    OpenMP_InReductionClause, OpenMP_MergeableClause,
677                    OpenMP_PriorityClause, OpenMP_PrivateClause,
678                    OpenMP_UntiedClause, OpenMP_DetachClause],
679                singleRegion = true> {
680  let summary = "task construct";
681  let description = [{
682    The task construct defines an explicit task.
683
684    For definitions of "undeferred task", "included task", "final task" and
685    "mergeable task", please check OpenMP Specification.
686
687    When an `if` clause is present on a task construct, and the value of
688    `if_expr` evaluates to `false`, an "undeferred task" is generated, and the
689    encountering thread must suspend the current task region, for which
690    execution cannot be resumed until execution of the structured block that is
691    associated with the generated task is completed.
692
693    The `in_reduction` clause specifies that this particular task (among all the
694    tasks in current taskgroup, if any) participates in a reduction.
695    `in_reduction_byref` indicates whether each reduction variable should
696    be passed by value or by reference.
697  }] # clausesDescription;
698
699  let builders = [
700    OpBuilder<(ins CArg<"const TaskOperands &">:$clauses)>
701  ];
702
703  let assemblyFormat = clausesAssemblyFormat # [{
704    custom<InReductionPrivateRegion>(
705        $region, $in_reduction_vars, type($in_reduction_vars),
706        $in_reduction_byref, $in_reduction_syms, $private_vars,
707        type($private_vars), $private_syms) attr-dict
708  }];
709
710  let hasVerifier = 1;
711}
712
713def TaskloopOp : OpenMP_Op<"taskloop", traits = [
714    AttrSizedOperandSegments, AutomaticAllocationScope,
715    DeclareOpInterfaceMethods<ComposableOpInterface>,
716    DeclareOpInterfaceMethods<LoopWrapperInterface>, NoTerminator,
717    RecursiveMemoryEffects, SingleBlock
718  ], clauses = [
719    OpenMP_AllocateClause, OpenMP_FinalClause, OpenMP_GrainsizeClause,
720    OpenMP_IfClause, OpenMP_InReductionClauseSkip<extraClassDeclaration = true>,
721    OpenMP_MergeableClause, OpenMP_NogroupClause, OpenMP_NumTasksClause,
722    OpenMP_PriorityClause, OpenMP_PrivateClause,
723    OpenMP_ReductionClauseSkip<extraClassDeclaration = true>,
724    OpenMP_UntiedClause
725  ], singleRegion = true> {
726  let summary = "taskloop construct";
727  let description = [{
728    The taskloop construct specifies that the iterations of one or more
729    associated loops will be executed in parallel using explicit tasks. The
730    iterations are distributed across tasks generated by the construct and
731    scheduled to be executed.
732
733    The body region can only contain a single block which must contain a single
734    operation. This operation must be another compatible loop wrapper or an
735    `omp.loop_nest`.
736
737    ```
738    omp.taskloop <clauses> {
739      omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
740        %a = load %arrA[%i1, %i2] : memref<?x?xf32>
741        %b = load %arrB[%i1, %i2] : memref<?x?xf32>
742        %sum = arith.addf %a, %b : f32
743        store %sum, %arrC[%i1, %i2] : memref<?x?xf32>
744        omp.yield
745      }
746    }
747    ```
748
749    For definitions of "undeferred task", "included task", "final task" and
750    "mergeable task", please check OpenMP Specification.
751
752    When an `if` clause is present on a taskloop construct, and if the `if`
753    clause expression evaluates to `false`, undeferred tasks are generated. The
754    use of a variable in an `if` clause expression of a taskloop construct
755    causes an implicit reference to the variable in all enclosing constructs.
756  }] # clausesDescription # [{
757    If an `in_reduction` clause is present on the taskloop construct, the
758    behavior is as if each generated task was defined by a task construct on
759    which an `in_reduction` clause with the same reduction operator and list
760    items is present. Thus, the generated tasks are participants of a reduction
761    previously defined by a reduction scoping clause. In this case, accumulator
762    variables are specified in `in_reduction_vars`, symbols referring to
763    reduction declarations in `in_reduction_syms` and `in_reduction_byref`
764    indicate for each reduction variable whether it should be passed by value or
765    by reference.
766
767    If a `reduction` clause is present on the taskloop construct, the behavior
768    is as if a `task_reduction` clause with the same reduction operator and list
769    items was applied to the implicit taskgroup construct enclosing the taskloop
770    construct. The taskloop construct executes as if each generated task was
771    defined by a task construct on which an `in_reduction` clause with the same
772    reduction operator and list items is present. Thus, the generated tasks are
773    participants of the reduction defined by the `task_reduction` clause that
774    was applied to the implicit taskgroup construct.
775  }];
776
777  let builders = [
778    OpBuilder<(ins CArg<"const TaskloopOperands &">:$clauses)>
779  ];
780
781  let assemblyFormat = clausesAssemblyFormat # [{
782    custom<InReductionPrivateReductionRegion>(
783        $region, $in_reduction_vars, type($in_reduction_vars),
784        $in_reduction_byref, $in_reduction_syms, $private_vars,
785        type($private_vars), $private_syms, $reduction_mod, $reduction_vars,
786        type($reduction_vars), $reduction_byref, $reduction_syms) attr-dict
787  }];
788
789  let extraClassDeclaration = [{
790    /// Returns the reduction variables
791    SmallVector<Value> getAllReductionVars();
792
793    // Define BlockArgOpenMPOpInterface methods here because they are not
794    // inherited from the respective clauses.
795    unsigned numInReductionBlockArgs() { return getInReductionVars().size(); }
796    unsigned numReductionBlockArgs() { return getReductionVars().size(); }
797
798    void getEffects(SmallVectorImpl<MemoryEffects::EffectInstance> &effects);
799  }] # clausesExtraClassDeclaration;
800
801  let hasVerifier = 1;
802  let hasRegionVerifier = 1;
803}
804
805def TaskgroupOp : OpenMP_Op<"taskgroup", traits = [
806    AttrSizedOperandSegments, AutomaticAllocationScope
807  ], clauses = [
808    OpenMP_AllocateClause, OpenMP_TaskReductionClause
809  ], singleRegion = true> {
810  let summary = "taskgroup construct";
811  let description = [{
812    The taskgroup construct specifies a wait on completion of child tasks of the
813    current task and their descendent tasks.
814
815    When a thread encounters a taskgroup construct, it starts executing the
816    region. All child tasks generated in the taskgroup region and all of their
817    descendants that bind to the same parallel region as the taskgroup region
818    are part of the taskgroup set associated with the taskgroup region. There is
819    an implicit task scheduling point at the end of the taskgroup region. The
820    current task is suspended at the task scheduling point until all tasks in
821    the taskgroup set complete execution.
822  }] # clausesDescription;
823
824  let builders = [
825    OpBuilder<(ins CArg<"const TaskgroupOperands &">:$clauses)>
826  ];
827
828  let assemblyFormat = clausesAssemblyFormat # [{
829    custom<TaskReductionRegion>(
830        $region, $task_reduction_vars, type($task_reduction_vars),
831        $task_reduction_byref, $task_reduction_syms) attr-dict
832  }];
833
834  let hasVerifier = 1;
835}
836
837//===----------------------------------------------------------------------===//
838// 2.10.4 taskyield Construct
839//===----------------------------------------------------------------------===//
840
841def TaskyieldOp : OpenMP_Op<"taskyield"> {
842  let summary = "taskyield construct";
843  let description = [{
844    The taskyield construct specifies that the current task can be suspended
845    in favor of execution of a different task.
846  }];
847
848  let assemblyFormat = "attr-dict";
849}
850
851//===----------------------------------------------------------------------===//
852// 2.13.7 flush Construct
853//===----------------------------------------------------------------------===//
854def FlushOp : OpenMP_Op<"flush", clauses = [
855    // TODO: Complete clause list (memory_order).
856  ]> {
857  let summary = "flush construct";
858  let description = [{
859    The flush construct executes the OpenMP flush operation. This operation
860    makes a thread's temporary view of memory consistent with memory and
861    enforces an order on the memory operations of the variables explicitly
862    specified or implied.
863  }] # clausesDescription;
864
865  let arguments = !con((ins Variadic<OpenMP_PointerLikeType>:$varList),
866                       clausesArgs);
867
868  // Override inherited assembly format to include `varList`.
869  let assemblyFormat = "( `(` $varList^ `:` type($varList) `)` )? attr-dict";
870
871  let extraClassDeclaration = [{
872    /// The number of variable operands.
873    unsigned getNumVariableOperands() {
874      return getOperation()->getNumOperands();
875    }
876    /// The i-th variable operand passed.
877    Value getVariableOperand(unsigned i) {
878      return getOperand(i);
879    }
880  }] # clausesExtraClassDeclaration;
881}
882
883//===----------------------------------------------------------------------===//
884// Map related constructs
885//===----------------------------------------------------------------------===//
886
887def MapBoundsOp : OpenMP_Op<"map.bounds",
888    [AttrSizedOperandSegments, NoMemoryEffect]> {
889  let summary = "Represents normalized bounds information for map clauses.";
890
891  let description = [{
892    This operation is a variation on the OpenACC dialects DataBoundsOp. Within
893    the OpenMP dialect it stores the bounds/range of data to be mapped to a
894    device specified by map clauses on target directives. Within
895    the OpenMP dialect, the MapBoundsOp is associated with MapInfoOp,
896    helping to store bounds information for the mapped variable.
897
898    It is used to support OpenMP array sectioning, Fortran pointer and
899    allocatable mapping and pointer/allocatable member of derived types.
900    In all cases the MapBoundsOp holds information on the section of
901    data to be mapped. Such as the upper bound and lower bound of the
902    section of data to be mapped. This information is currently
903    utilised by the LLVM-IR lowering to help generate instructions to
904    copy data to and from the device when processing target operations.
905
906    The example below copys a section of a 10-element array; all except the
907    first element, utilising OpenMP array sectioning syntax where array
908    subscripts are provided to specify the bounds to be mapped to device.
909    To simplify the examples, the constants are used directly, in reality
910    they will be MLIR SSA values.
911
912    C++:
913    ```
914    int array[10];
915    #pragma target map(array[1:9])
916    ```
917    =>
918    ```mlir
919    omp.map.bounds lower_bound(1) upper_bound(9) extent(9) start_idx(0)
920    ```
921
922    Fortran:
923    ```
924    integer :: array(1:10)
925    !$target map(array(2:10))
926    ```
927    =>
928    ```mlir
929    omp.map.bounds lower_bound(1) upper_bound(9) extent(9) start_idx(1)
930    ```
931
932    For Fortran pointers and allocatables (as well as those that are
933    members of derived types) the bounds information is provided by
934    the Fortran compiler and runtime through descriptor information.
935
936    A basic pointer example can be found below (constants again
937    provided for simplicity, where in reality SSA values will be
938    used, in this case that point to data yielded by Fortran's
939    descriptors):
940
941    Fortran:
942    ```
943    integer, pointer :: ptr(:)
944    allocate(ptr(10))
945    !$target map(ptr)
946    ```
947    =>
948    ```mlir
949    omp.map.bounds lower_bound(0) upper_bound(9) extent(10) start_idx(1)
950    ```
951
952    This operation records the bounds information in a normalized fashion
953    (zero-based). This works well with the `PointerLikeType`
954    requirement in data clauses - since a `lower_bound` of 0 means looking
955    at data at the zero offset from pointer.
956
957    This operation must have an `upper_bound` or `extent` (or both are allowed -
958    but not checked for consistency). When the source language's arrays are
959    not zero-based, the `start_idx` must specify the zero-position index.
960  }];
961
962  let arguments = (ins Optional<IntLikeType>:$lower_bound,
963                       Optional<IntLikeType>:$upper_bound,
964                       Optional<IntLikeType>:$extent,
965                       Optional<IntLikeType>:$stride,
966                       DefaultValuedAttr<BoolAttr, "false">:$stride_in_bytes,
967                       Optional<IntLikeType>:$start_idx);
968  let results = (outs OpenMP_MapBoundsType:$result);
969
970  let assemblyFormat = [{
971    oilist(
972        `lower_bound` `(` $lower_bound `:` type($lower_bound) `)`
973      | `upper_bound` `(` $upper_bound `:` type($upper_bound) `)`
974      | `extent` `(` $extent `:` type($extent) `)`
975      | `stride` `(` $stride `:` type($stride) `)`
976      | `start_idx` `(` $start_idx `:` type($start_idx) `)`
977    ) attr-dict
978  }];
979
980  let extraClassDeclaration = [{
981    /// The number of variable operands.
982    unsigned getNumVariableOperands() {
983      return getNumOperands();
984    }
985
986    /// The i-th variable operand passed.
987    Value getVariableOperand(unsigned i) {
988      return getOperands()[i];
989    }
990  }];
991
992  let hasVerifier = 1;
993}
994
995def MapInfoOp : OpenMP_Op<"map.info", [AttrSizedOperandSegments]> {
996  let arguments = (ins OpenMP_PointerLikeType:$var_ptr,
997                       TypeAttr:$var_type,
998                       Optional<OpenMP_PointerLikeType>:$var_ptr_ptr,
999                       Variadic<OpenMP_PointerLikeType>:$members,
1000                       OptionalAttr<IndexListArrayAttr>:$members_index,
1001                       Variadic<OpenMP_MapBoundsType>:$bounds, /* rank-0 to rank-{n-1} */
1002                       OptionalAttr<UI64Attr>:$map_type,
1003                       OptionalAttr<VariableCaptureKindAttr>:$map_capture_type,
1004                       OptionalAttr<StrAttr>:$name,
1005                       DefaultValuedAttr<BoolAttr, "false">:$partial_map);
1006  let results = (outs OpenMP_PointerLikeType:$omp_ptr);
1007
1008  let description = [{
1009    The MapInfoOp captures information relating to individual OpenMP map clauses
1010    that are applied to certain OpenMP directives such as Target and Target Data.
1011
1012    For example, the map type modifier; such as from, tofrom and to, the variable
1013    being captured or the bounds of an array section being mapped.
1014
1015    It can be used to capture both implicit and explicit map information, where
1016    explicit is an argument directly specified to an OpenMP map clause or implicit
1017    where a variable is utilised in a target region but is defined externally to
1018    the target region.
1019
1020    This map information is later used to aid the lowering of the target operations
1021    they are attached to providing argument input and output context for kernels
1022    generated or the target data mapping environment.
1023
1024    Example (Fortran):
1025
1026    ```
1027    integer :: index
1028    !$target map(to: index)
1029    ```
1030    =>
1031    ```mlir
1032    omp.map.info var_ptr(%index_ssa) map_type(to) map_capture_type(ByRef)
1033      name(index)
1034    ```
1035
1036    Description of arguments:
1037    - `var_ptr`: The address of variable to copy.
1038    - `var_type`: The type of the variable to copy.
1039    - `var_ptr_ptr`: Used when the variable copied is a member of a class, structure
1040      or derived type and refers to the originating struct.
1041    - `members`: Used to indicate mapped child members for the current MapInfoOp,
1042       represented as other MapInfoOp's, utilised in cases where a parent structure
1043       type and members of the structure type are being mapped at the same time.
1044       For example: map(to: parent, parent->member, parent->member2[:10])
1045    - `members_index`: Used to indicate the ordering of members within the containing
1046       parent (generally a record type such as a structure, class or derived type),
1047       e.g. struct {int x, float y, double z}, x would be 0, y would be 1, and z
1048       would be 2. This aids the mapping.
1049    - `bounds`: Used when copying slices of array's, pointers or pointer members of
1050       objects (e.g. derived types or classes), indicates the bounds to be copied
1051       of the variable. When it's an array slice it is in rank order where rank 0
1052       is the inner-most dimension.
1053    - 'map_type': OpenMP map type for this map capture, for example: from, to and
1054       always. It's a bitfield composed of the OpenMP runtime flags stored in
1055       OpenMPOffloadMappingFlags.
1056    - 'map_capture_type': Capture type for the variable e.g. this, byref, byvalue, byvla
1057       this can affect how the variable is lowered.
1058    - `name`: Holds the name of variable as specified in user clause (including bounds).
1059    - `partial_map`: The record type being mapped will not be mapped in its entirety,
1060       it may be used however, in a mapping to bind it's mapped components together.
1061  }];
1062
1063  let assemblyFormat = [{
1064    `var_ptr` `(` $var_ptr `:` type($var_ptr) `,` $var_type `)`
1065    oilist(
1066        `var_ptr_ptr` `(` $var_ptr_ptr `:` type($var_ptr_ptr) `)`
1067      | `map_clauses` `(` custom<MapClause>($map_type) `)`
1068      | `capture` `(` custom<CaptureType>($map_capture_type) `)`
1069      | `members` `(` $members `:` custom<MembersIndex>($members_index) `:` type($members) `)`
1070      | `bounds` `(` $bounds `)`
1071    ) `->` type($omp_ptr) attr-dict
1072  }];
1073
1074  let extraClassDeclaration = [{
1075    /// The number of variable operands.
1076    unsigned getNumVariableOperands() {
1077      return getNumOperands();
1078    }
1079
1080    /// The i-th variable operand passed.
1081    Value getVariableOperand(unsigned i) {
1082      return getOperands()[i];
1083    }
1084  }];
1085}
1086
1087//===---------------------------------------------------------------------===//
1088// 2.14.2 target data Construct
1089//===---------------------------------------------------------------------===//
1090
1091def TargetDataOp: OpenMP_Op<"target_data", traits = [
1092    AttrSizedOperandSegments
1093  ], clauses = [
1094    OpenMP_DeviceClause, OpenMP_IfClause, OpenMP_MapClause,
1095    OpenMP_UseDeviceAddrClause, OpenMP_UseDevicePtrClause
1096  ], singleRegion = true> {
1097  let summary = "target data construct";
1098  let description = [{
1099    Map variables to a device data environment for the extent of the region.
1100
1101    The omp target data directive maps variables to a device data
1102    environment, and defines the lexical scope of the data environment
1103    that is created. The omp target data directive can reduce data copies
1104    to and from the offloading device when multiple target regions are using
1105    the same data.
1106
1107    The optional `if_expr` parameter specifies a boolean result of a conditional
1108    check. If this value is 1 or is not provided then the target region runs on
1109    a device, if it is 0 then the target region is executed on the host device.
1110  }] # clausesDescription;
1111
1112  let builders = [
1113    OpBuilder<(ins CArg<"const TargetDataOperands &">:$clauses)>
1114  ];
1115
1116  let assemblyFormat = clausesAssemblyFormat # [{
1117    custom<UseDeviceAddrUseDevicePtrRegion>(
1118        $region, $use_device_addr_vars, type($use_device_addr_vars),
1119        $use_device_ptr_vars, type($use_device_ptr_vars)) attr-dict
1120  }];
1121
1122  let hasVerifier = 1;
1123}
1124
1125//===---------------------------------------------------------------------===//
1126// 2.14.3 target enter data Construct
1127//===---------------------------------------------------------------------===//
1128
1129def TargetEnterDataOp: OpenMP_Op<"target_enter_data", traits = [
1130    AttrSizedOperandSegments
1131  ], clauses = [
1132    OpenMP_DependClause, OpenMP_DeviceClause, OpenMP_IfClause, OpenMP_MapClause,
1133    OpenMP_NowaitClause
1134  ]> {
1135  let summary = "target enter data construct";
1136  let description = [{
1137    The target enter data directive specifies that variables are mapped to
1138    a device data environment. The target enter data directive is a
1139    stand-alone directive.
1140
1141    The optional `if_expr` parameter specifies a boolean result of a conditional
1142    check. If this value is 1 or is not provided then the target region runs on
1143    a device, if it is 0 then the target region is executed on the host device.
1144  }] # clausesDescription;
1145
1146  let builders = [
1147    OpBuilder<(ins CArg<"const TargetEnterExitUpdateDataOperands &">:$clauses)>
1148  ];
1149
1150  let hasVerifier = 1;
1151}
1152
1153//===---------------------------------------------------------------------===//
1154// 2.14.4 target exit data Construct
1155//===---------------------------------------------------------------------===//
1156
1157def TargetExitDataOp: OpenMP_Op<"target_exit_data", traits = [
1158    AttrSizedOperandSegments
1159  ], clauses = [
1160    OpenMP_DependClause, OpenMP_DeviceClause, OpenMP_IfClause, OpenMP_MapClause,
1161    OpenMP_NowaitClause
1162  ]> {
1163  let summary = "target exit data construct";
1164  let description = [{
1165    The target exit data directive specifies that variables are mapped to a
1166    device data environment. The target exit data directive is
1167    a stand-alone directive.
1168
1169    The optional `if_expr` parameter specifies a boolean result of a conditional
1170    check. If this value is 1 or is not provided then the target region runs on
1171    a device, if it is 0 then the target region is executed on the host device.
1172  }] # clausesDescription;
1173
1174  let builders = [
1175    OpBuilder<(ins CArg<"const TargetEnterExitUpdateDataOperands &">:$clauses)>
1176  ];
1177
1178  let hasVerifier = 1;
1179}
1180
1181//===---------------------------------------------------------------------===//
1182// 2.14.6 target update Construct
1183//===---------------------------------------------------------------------===//
1184
1185def TargetUpdateOp: OpenMP_Op<"target_update", traits = [
1186    AttrSizedOperandSegments
1187  ], clauses = [
1188    OpenMP_DependClause, OpenMP_DeviceClause, OpenMP_IfClause, OpenMP_MapClause,
1189    OpenMP_NowaitClause
1190  ]> {
1191  let summary = "target update construct";
1192  let description = [{
1193    The target update directive makes the corresponding list items in the device
1194    data environment consistent with their original list items, according to the
1195    specified motion clauses. The target update construct is a stand-alone
1196    directive.
1197
1198    The optional `if_expr` parameter specifies a boolean result of a conditional
1199    check. If this value is 1 or is not provided then the target region runs on
1200    a device, if it is 0 then the target region is executed on the host device.
1201
1202    We use `MapInfoOp` to model the motion clauses and their modifiers. Even
1203    though the spec differentiates between map-types & map-type-modifiers vs.
1204    motion-clauses & motion-modifiers, the motion clauses and their modifiers
1205    are a subset of map types and their modifiers. The subset relation is
1206    handled in during verification to make sure the restrictions for target
1207    update are respected.
1208  }] # clausesDescription;
1209
1210  let builders = [
1211    OpBuilder<(ins CArg<"const TargetEnterExitUpdateDataOperands &">:$clauses)>
1212  ];
1213
1214  let hasVerifier = 1;
1215}
1216
1217//===----------------------------------------------------------------------===//
1218// 2.14.5 target construct
1219//===----------------------------------------------------------------------===//
1220
1221def TargetOp : OpenMP_Op<"target", traits = [
1222    AttrSizedOperandSegments, BlockArgOpenMPOpInterface, IsolatedFromAbove,
1223    OutlineableOpenMPOpInterface
1224  ], clauses = [
1225    // TODO: Complete clause list (defaultmap, uses_allocators).
1226    OpenMP_AllocateClause, OpenMP_BareClause, OpenMP_DependClause,
1227    OpenMP_DeviceClause, OpenMP_HasDeviceAddrClause, OpenMP_HostEvalClause,
1228    OpenMP_IfClause, OpenMP_InReductionClause, OpenMP_IsDevicePtrClause,
1229    OpenMP_MapClauseSkip<assemblyFormat = true>, OpenMP_NowaitClause,
1230    OpenMP_PrivateClause, OpenMP_ThreadLimitClause
1231  ], singleRegion = true> {
1232  let summary = "target construct";
1233  let description = [{
1234    The target construct includes a region of code which is to be executed
1235    on a device.
1236
1237    The optional `if_expr` parameter specifies a boolean result of a conditional
1238    check. If this value is 1 or is not provided then the target region runs on
1239    a device, if it is 0 then the target region is executed on the host device.
1240
1241    The `private_maps` attribute connects `private` operands to their corresponding
1242    `map` operands. For `private` operands that require a map, the value of the
1243    corresponding element in the attribute is the index of the `map` operand
1244    (relative to other `map` operands not the whole operands of the operation). For
1245    `private` opernads that do not require a map, this value is -1 (which is omitted
1246    from the assembly foramt printing).
1247  }] # clausesDescription;
1248
1249  let arguments = !con(clausesArgs,
1250                       (ins OptionalAttr<DenseI64ArrayAttr>:$private_maps));
1251
1252  let builders = [
1253    OpBuilder<(ins CArg<"const TargetOperands &">:$clauses)>
1254  ];
1255
1256  let extraClassDeclaration = [{
1257    unsigned numMapBlockArgs() { return getMapVars().size(); }
1258
1259    mlir::Value getMappedValueForPrivateVar(unsigned privVarIdx) {
1260      std::optional<DenseI64ArrayAttr> privateMapIdices = getPrivateMapsAttr();
1261
1262      if (!privateMapIdices.has_value())
1263        return {};
1264
1265      int64_t mapInfoOpIdx = (*privateMapIdices)[privVarIdx];
1266
1267      if (mapInfoOpIdx == -1)
1268        return {};
1269
1270      return getMapVars()[mapInfoOpIdx];
1271    }
1272
1273    /// Returns the innermost OpenMP dialect operation captured by this target
1274    /// construct. For an operation to be detected as captured, it must be
1275    /// inside a (possibly multi-level) nest of OpenMP dialect operation's
1276    /// regions where none of these levels contain other operations considered
1277    /// not-allowed for these purposes (i.e. only terminator operations are
1278    /// allowed from the OpenMP dialect, and other dialect's operations are
1279    /// allowed as long as they don't have a memory write effect).
1280    ///
1281    /// If there are omp.loop_nest operations in the sequence of nested
1282    /// operations, the top level one will be the one captured.
1283    Operation *getInnermostCapturedOmpOp();
1284
1285    /// Infers the kernel type (Generic, SPMD or Generic-SPMD) based on the
1286    /// contents of the target region.
1287    llvm::omp::OMPTgtExecModeFlags getKernelExecFlags();
1288  }] # clausesExtraClassDeclaration;
1289
1290  let assemblyFormat = clausesAssemblyFormat # [{
1291    custom<HostEvalInReductionMapPrivateRegion>(
1292        $region, $host_eval_vars, type($host_eval_vars), $in_reduction_vars,
1293        type($in_reduction_vars), $in_reduction_byref, $in_reduction_syms,
1294        $map_vars, type($map_vars), $private_vars, type($private_vars),
1295        $private_syms, $private_maps) attr-dict
1296  }];
1297
1298  let hasVerifier = 1;
1299  let hasRegionVerifier = 1;
1300}
1301
1302
1303//===----------------------------------------------------------------------===//
1304// 2.16 master Construct
1305//===----------------------------------------------------------------------===//
1306def MasterOp : OpenMP_Op<"master", singleRegion = true> {
1307  let summary = "master construct";
1308  let description = [{
1309    The master construct specifies a structured block that is executed by
1310    the master thread of the team.
1311  }];
1312
1313  let assemblyFormat = "$region attr-dict";
1314}
1315
1316//===----------------------------------------------------------------------===//
1317// 2.17.1 critical Construct
1318//===----------------------------------------------------------------------===//
1319def CriticalDeclareOp : OpenMP_Op<"critical.declare", clauses = [
1320    OpenMP_CriticalNameClause, OpenMP_HintClause
1321  ]> {
1322  let summary = "declares a named critical section.";
1323  let description = [{
1324    Declares a named critical section.
1325  }] # clausesDescription;
1326
1327  let builders = [
1328    OpBuilder<(ins CArg<"const CriticalDeclareOperands &">:$clauses)>
1329  ];
1330
1331  let hasVerifier = 1;
1332}
1333
1334
1335def CriticalOp : OpenMP_Op<"critical", [
1336    DeclareOpInterfaceMethods<SymbolUserOpInterface>
1337  ], singleRegion = 1> {
1338  let summary = "critical construct";
1339  let description = [{
1340    The critical construct imposes a restriction on the associated structured
1341    block (region) to be executed by only a single thread at a time.
1342
1343    The optional `name` argument of critical constructs is used to identify
1344    them. Unnamed critical constructs behave as though an identical name was
1345    specified.
1346  }];
1347
1348  let arguments = (ins OptionalAttr<FlatSymbolRefAttr>:$name);
1349
1350  let assemblyFormat = [{
1351    (`(` $name^ `)`)? $region attr-dict
1352  }];
1353}
1354
1355//===----------------------------------------------------------------------===//
1356// 2.17.2 barrier Construct
1357//===----------------------------------------------------------------------===//
1358
1359def BarrierOp : OpenMP_Op<"barrier"> {
1360  let summary = "barrier construct";
1361  let description = [{
1362    The barrier construct specifies an explicit barrier at the point at which
1363    the construct appears.
1364  }];
1365
1366  let assemblyFormat = "attr-dict";
1367}
1368
1369//===----------------------------------------------------------------------===//
1370// [5.1] 2.19.9 ordered Construct
1371//===----------------------------------------------------------------------===//
1372
1373def OrderedOp : OpenMP_Op<"ordered", clauses = [OpenMP_DoacrossClause]> {
1374  let summary = "ordered construct without region";
1375  let description = [{
1376    The ordered construct without region is a stand-alone directive that
1377    specifies cross-iteration dependencies in a doacross loop nest.
1378  }] # clausesDescription;
1379
1380  let builders = [
1381    OpBuilder<(ins CArg<"const OrderedOperands &">:$clauses)>
1382  ];
1383
1384  let hasVerifier = 1;
1385}
1386
1387def OrderedRegionOp : OpenMP_Op<"ordered.region", clauses = [
1388    OpenMP_ParallelizationLevelClause
1389  ], singleRegion = true> {
1390  let summary = "ordered construct with region";
1391  let description = [{
1392    The ordered construct with region specifies a structured block in a
1393    worksharing-loop, SIMD, or worksharing-loop SIMD region that is executed in
1394    the order of the loop iterations.
1395  }] # clausesDescription;
1396
1397  let builders = [
1398    OpBuilder<(ins CArg<"const OrderedRegionOperands &">:$clauses)>
1399  ];
1400
1401  let hasVerifier = 1;
1402}
1403
1404//===----------------------------------------------------------------------===//
1405// 2.17.5 taskwait Construct
1406//===----------------------------------------------------------------------===//
1407
1408def TaskwaitOp : OpenMP_Op<"taskwait", clauses = [
1409    OpenMP_DependClause, OpenMP_NowaitClause
1410  ]> {
1411  let summary = "taskwait construct";
1412  let description = [{
1413    The taskwait construct specifies a wait on the completion of child tasks
1414    of the current task.
1415  }] # clausesDescription;
1416
1417  let builders = [
1418    OpBuilder<(ins CArg<"const TaskwaitOperands &">:$clauses)>
1419  ];
1420}
1421
1422//===----------------------------------------------------------------------===//
1423// 2.17.7 atomic construct
1424//===----------------------------------------------------------------------===//
1425
1426// In the OpenMP Specification, atomic construct has an `atomic-clause` which
1427// can take the values `read`, `write`, `update` and `capture`. These four
1428// kinds of atomic constructs are fundamentally independent and are handled
1429// separately while lowering. Having four separate operations (one for each
1430// value of the clause) here decomposes handling of this construct into a
1431// two-step process.
1432
1433def AtomicReadOp : OpenMP_Op<"atomic.read", traits = [
1434    AtomicReadOpInterface
1435  ], clauses = [
1436    OpenMP_HintClause, OpenMP_MemoryOrderClause
1437  ]> {
1438  let summary = "performs an atomic read";
1439  let description = [{
1440    This operation performs an atomic read.
1441
1442    The operand `x` is the address from where the value is atomically read.
1443    The operand `v` is the address where the value is stored after reading.
1444  }] # clausesDescription;
1445
1446  let arguments = !con((ins OpenMP_PointerLikeType:$x,
1447                            OpenMP_PointerLikeType:$v,
1448                            TypeAttr:$element_type), clausesArgs);
1449
1450  // Override clause-based assemblyFormat.
1451  let assemblyFormat = "$v `=` $x" # clausesReqAssemblyFormat # " oilist(" #
1452    clausesOptAssemblyFormat #
1453    ") `:` type($v) `,` type($x) `,` $element_type attr-dict";
1454
1455  let extraClassDeclaration = [{
1456    /// The number of variable operands.
1457    unsigned getNumVariableOperands() {
1458      assert(getX() && "expected 'x' operand");
1459      assert(getV() && "expected 'v' operand");
1460      return 2;
1461    }
1462
1463    /// The i-th variable operand passed.
1464    Value getVariableOperand(unsigned i) {
1465      assert(i < 2 && "invalid index position for an operand");
1466      return i == 0 ? getX() : getV();
1467    }
1468  }] # clausesExtraClassDeclaration;
1469
1470  let hasVerifier = 1;
1471}
1472
1473def AtomicWriteOp : OpenMP_Op<"atomic.write", traits = [
1474    AtomicWriteOpInterface
1475  ], clauses = [
1476    OpenMP_HintClause, OpenMP_MemoryOrderClause
1477  ]> {
1478  let summary = "performs an atomic write";
1479  let description = [{
1480    This operation performs an atomic write.
1481
1482    The operand `x` is the address to where the `expr` is atomically
1483    written w.r.t. multiple threads. The evaluation of `expr` need not be
1484    atomic w.r.t. the write to address. In general, the type(x) must
1485    dereference to type(expr).
1486  }] # clausesDescription;
1487
1488  let arguments = !con((ins OpenMP_PointerLikeType:$x,
1489                            AnyType:$expr), clausesArgs);
1490
1491  // Override clause-based assemblyFormat.
1492  let assemblyFormat = "$x `=` $expr" # clausesReqAssemblyFormat # " oilist(" #
1493    clausesOptAssemblyFormat # ") `:` type($x) `,` type($expr) attr-dict";
1494
1495  let extraClassDeclaration = [{
1496    /// The number of variable operands.
1497    unsigned getNumVariableOperands() {
1498      assert(getX() && "expected address operand");
1499      assert(getExpr() && "expected value operand");
1500      return 2;
1501    }
1502
1503    /// The i-th variable operand passed.
1504    Value getVariableOperand(unsigned i) {
1505      assert(i < 2 && "invalid index position for an operand");
1506      return i == 0 ? getX() : getExpr();
1507    }
1508  }] # clausesExtraClassDeclaration;
1509
1510  let hasVerifier = 1;
1511}
1512
1513def AtomicUpdateOp : OpenMP_Op<"atomic.update", traits = [
1514    AtomicUpdateOpInterface, RecursiveMemoryEffects,
1515    SingleBlockImplicitTerminator<"YieldOp">
1516  ], clauses = [
1517    OpenMP_HintClause, OpenMP_MemoryOrderClause
1518  ], singleRegion = 1> {
1519  let summary = "performs an atomic update";
1520  let description = [{
1521    This operation performs an atomic update.
1522
1523    The operand `x` is exactly the same as the operand `x` in the OpenMP
1524    Standard (OpenMP 5.0, section 2.17.7). It is the address of the variable
1525    that is being updated. `x` is atomically read/written.
1526
1527    The region describes how to update the value of `x`. It takes the value at
1528    `x` as an input and must yield the updated value. Only the update to `x` is
1529    atomic. Generally the region must have only one instruction, but can
1530    potentially have more than one instructions too. The update is sematically
1531    similar to a compare-exchange loop based atomic update.
1532
1533    The syntax of atomic update operation is different from atomic read and
1534    atomic write operations. This is because only the host dialect knows how to
1535    appropriately update a value. For example, while generating LLVM IR, if
1536    there are no special `atomicrmw` instructions for the operation-type
1537    combination in atomic update, a compare-exchange loop is generated, where
1538    the core update operation is directly translated like regular operations by
1539    the host dialect. The front-end must handle semantic checks for allowed
1540    operations.
1541  }] # clausesDescription;
1542
1543  let arguments = !con((ins Arg<OpenMP_PointerLikeType,
1544                                "Address of variable to be updated",
1545                                [MemRead, MemWrite]>:$x), clausesArgs);
1546
1547  // Override region definition.
1548  let regions = (region SizedRegion<1>:$region);
1549
1550  // Override clause-based assemblyFormat.
1551  let assemblyFormat = clausesAssemblyFormat #
1552    "$x `:` type($x) $region attr-dict";
1553
1554  let extraClassDeclaration = [{
1555    /// The number of variable operands.
1556    unsigned getNumVariableOperands() {
1557      assert(getX() && "expected 'x' operand");
1558      return 1;
1559    }
1560
1561    /// The i-th variable operand passed.
1562    Value getVariableOperand(unsigned i) {
1563      assert(i == 0 && "invalid index position for an operand");
1564      return getX();
1565    }
1566  }] # clausesExtraClassDeclaration;
1567
1568  let hasVerifier = 1;
1569  let hasRegionVerifier = 1;
1570  let hasCanonicalizeMethod = 1;
1571}
1572
1573def AtomicCaptureOp : OpenMP_Op<"atomic.capture", traits = [
1574    AtomicCaptureOpInterface, RecursiveMemoryEffects,
1575    SingleBlockImplicitTerminator<"TerminatorOp">
1576  ], clauses = [
1577    OpenMP_HintClause, OpenMP_MemoryOrderClause
1578  ], singleRegion = 1> {
1579  let summary = "performs an atomic capture";
1580  let description = [{
1581    This operation performs an atomic capture.
1582
1583    The region has the following allowed forms:
1584    ```
1585      omp.atomic.capture {
1586        omp.atomic.update ...
1587        omp.atomic.read ...
1588        omp.terminator
1589      }
1590
1591      omp.atomic.capture {
1592        omp.atomic.read ...
1593        omp.atomic.update ...
1594        omp.terminator
1595      }
1596
1597      omp.atomic.capture {
1598        omp.atomic.read ...
1599        omp.atomic.write ...
1600        omp.terminator
1601      }
1602    ```
1603  }] # clausesDescription;
1604
1605  // Override region definition.
1606  let regions = (region SizedRegion<1>:$region);
1607
1608  let extraClassDeclaration = [{
1609    /// Returns the `atomic.read` operation inside the region, if any.
1610    /// Otherwise, it returns nullptr.
1611    AtomicReadOp getAtomicReadOp();
1612
1613    /// Returns the `atomic.write` operation inside the region, if any.
1614    /// Otherwise, it returns nullptr.
1615    AtomicWriteOp getAtomicWriteOp();
1616
1617    /// Returns the `atomic.update` operation inside the region, if any.
1618    /// Otherwise, it returns nullptr.
1619    AtomicUpdateOp getAtomicUpdateOp();
1620  }] # clausesExtraClassDeclaration;
1621
1622  let hasRegionVerifier = 1;
1623  let hasVerifier = 1;
1624}
1625
1626//===----------------------------------------------------------------------===//
1627// [5.1] 2.21.2 threadprivate Directive
1628//===----------------------------------------------------------------------===//
1629
1630def ThreadprivateOp : OpenMP_Op<"threadprivate",
1631                                [AllTypesMatch<["sym_addr", "tls_addr"]>]> {
1632  let summary = "threadprivate directive";
1633  let description = [{
1634    The threadprivate directive specifies that variables are replicated, with
1635    each thread having its own copy.
1636
1637    The current implementation uses the OpenMP runtime to provide thread-local
1638    storage (TLS). Using the TLS feature of the LLVM IR will be supported in
1639    future.
1640
1641    This operation takes in the address of a symbol that represents the original
1642    variable and returns the address of its TLS. All occurrences of
1643    threadprivate variables in a parallel region should use the TLS returned by
1644    this operation.
1645
1646    The `sym_addr` refers to the address of the symbol, which is a pointer to
1647    the original variable.
1648  }];
1649
1650  let arguments = (ins OpenMP_PointerLikeType:$sym_addr);
1651  let results = (outs OpenMP_PointerLikeType:$tls_addr);
1652  let assemblyFormat = [{
1653    $sym_addr `:` type($sym_addr) `->` type($tls_addr) attr-dict
1654  }];
1655  let extraClassDeclaration = [{
1656    /// The number of variable operands.
1657    unsigned getNumVariableOperands() {
1658      assert(getSymAddr() && "expected one variable operand");
1659      return 1;
1660    }
1661
1662    /// The i-th variable operand passed.
1663    Value getVariableOperand(unsigned i) {
1664      assert(i == 0 && "invalid index position for an operand");
1665      return getSymAddr();
1666    }
1667  }];
1668}
1669
1670//===----------------------------------------------------------------------===//
1671// 2.18.1 Cancel Construct
1672//===----------------------------------------------------------------------===//
1673def CancelOp : OpenMP_Op<"cancel", clauses = [
1674    OpenMP_CancelDirectiveNameClause, OpenMP_IfClause
1675  ]> {
1676  let summary = "cancel directive";
1677  let description = [{
1678    The cancel construct activates cancellation of the innermost enclosing
1679    region of the type specified.
1680  }] # clausesDescription;
1681
1682  let builders = [
1683    OpBuilder<(ins CArg<"const CancelOperands &">:$clauses)>
1684  ];
1685
1686  let hasVerifier = 1;
1687}
1688
1689//===----------------------------------------------------------------------===//
1690// 2.18.2 Cancellation Point Construct
1691//===----------------------------------------------------------------------===//
1692def CancellationPointOp : OpenMP_Op<"cancellation_point", clauses = [
1693    OpenMP_CancelDirectiveNameClause
1694  ]> {
1695  let summary = "cancellation point directive";
1696  let description = [{
1697    The cancellation point construct introduces a user-defined cancellation
1698    point at which implicit or explicit tasks check if cancellation of the
1699    innermost enclosing region of the type specified has been activated.
1700  }] # clausesDescription;
1701
1702  let builders = [
1703    OpBuilder<(ins CArg<"const CancellationPointOperands &">:$clauses)>
1704  ];
1705
1706  let hasVerifier = 1;
1707}
1708
1709def ScanOp : OpenMP_Op<"scan", [
1710    AttrSizedOperandSegments, MemoryEffects<[MemWrite]>
1711  ], clauses = [
1712    OpenMP_InclusiveClause, OpenMP_ExclusiveClause]> {
1713  let summary = "scan directive";
1714  let description = [{
1715    The scan directive allows to specify scan reductions. It should be
1716    enclosed within a parent directive along with which a reduction clause
1717    with `inscan` modifier must be specified. The scan directive allows to
1718    split code blocks into input phase and scan phase in the region
1719    enclosed by the parent.
1720  }] # clausesDescription;
1721
1722  let builders = [
1723    OpBuilder<(ins CArg<"const ScanOperands &">:$clauses)>
1724  ];
1725
1726  let hasVerifier = 1;
1727}
1728
1729//===----------------------------------------------------------------------===//
1730// 2.19.5.7 declare reduction Directive
1731//===----------------------------------------------------------------------===//
1732
1733def DeclareReductionOp : OpenMP_Op<"declare_reduction", [IsolatedFromAbove,
1734                                                         RecipeInterface,
1735                                                         Symbol]> {
1736  let summary = "declares a reduction kind";
1737  let description = [{
1738    Declares an OpenMP reduction kind. This requires two mandatory and three
1739    optional regions.
1740
1741      1. The optional alloc region specifies how to allocate the thread-local
1742         reduction value. This region should not contain control flow and all
1743         IR should be suitable for inlining straight into an entry block. In
1744         the common case this is expected to contain only allocas. It is
1745         expected to `omp.yield` the allocated value on all control paths.
1746         If allocation is conditional (e.g. only allocate if the mold is
1747         allocated), this should be done in the initilizer region and this
1748         region not included. The alloc region is not used for by-value
1749         reductions (where allocation is implicit).
1750      2. The initializer region specifies how to initialize the thread-local
1751         reduction value. This is usually the neutral element of the reduction.
1752         For convenience, the region has an argument that contains the value
1753         of the reduction accumulator at the start of the reduction. If an alloc
1754         region is specified, there is a second block argument containing the
1755         address of the allocated memory. The initializer region is expected to
1756         `omp.yield` the new value on all control flow paths.
1757      3. The reduction region specifies how to combine two values into one, i.e.
1758         the reduction operator. It accepts the two values as arguments and is
1759         expected to `omp.yield` the combined value on all control flow paths.
1760      4. The atomic reduction region is optional and specifies how two values
1761         can be combined atomically given local accumulator variables. It is
1762         expected to store the combined value in the first accumulator variable.
1763      5. The cleanup region is optional and specifies how to clean up any memory
1764         allocated by the initializer region. The region has an argument that
1765         contains the value of the thread-local reduction accumulator. This will
1766         be executed after the reduction has completed.
1767
1768    Note that the MLIR type system does not allow for type-polymorphic
1769    reductions. Separate reduction declarations should be created for different
1770    element and accumulator types.
1771
1772    For initializer and reduction regions, the operand to `omp.yield` must
1773    match the parent operation's results.
1774  }];
1775
1776  let arguments = (ins SymbolNameAttr:$sym_name,
1777                       TypeAttr:$type);
1778
1779  let regions = (region MaxSizedRegion<1>:$allocRegion,
1780                        AnyRegion:$initializerRegion,
1781                        AnyRegion:$reductionRegion,
1782                        AnyRegion:$atomicReductionRegion,
1783                        AnyRegion:$cleanupRegion);
1784
1785  let assemblyFormat = "$sym_name `:` $type attr-dict-with-keyword "
1786                       "( `alloc` $allocRegion^ )? "
1787                       "`init` $initializerRegion "
1788                       "`combiner` $reductionRegion "
1789                       "( `atomic` $atomicReductionRegion^ )? "
1790                       "( `cleanup` $cleanupRegion^ )? ";
1791
1792  let extraClassDeclaration = [{
1793    BlockArgument getAllocMoldArg() {
1794      auto &region = getAllocRegion();
1795      return region.empty() ? nullptr : region.getArgument(0);
1796    }
1797    BlockArgument getInitializerMoldArg() {
1798      return getInitializerRegion().getArgument(0);
1799    }
1800    BlockArgument getInitializerAllocArg() {
1801      return getAllocRegion().empty() ?
1802          nullptr : getInitializerRegion().getArgument(1);
1803    }
1804    BlockArgument getReductionLhsArg() {
1805      return getReductionRegion().getArgument(0);
1806    }
1807    BlockArgument getReductionRhsArg() {
1808      return getReductionRegion().getArgument(1);
1809    }
1810    BlockArgument getAtomicReductionLhsArg() {
1811      auto &region = getAtomicReductionRegion();
1812      return region.empty() ? nullptr : region.getArgument(0);
1813    }
1814    BlockArgument getAtomicReductionRhsArg() {
1815      auto &region = getAtomicReductionRegion();
1816      return region.empty() ? nullptr : region.getArgument(1);
1817    }
1818    BlockArgument getCleanupAllocArg() {
1819      auto &region = getCleanupRegion();
1820      return region.empty() ? nullptr : region.getArgument(0);
1821    }
1822
1823    PointerLikeType getAccumulatorType() {
1824      if (getAtomicReductionRegion().empty())
1825        return {};
1826
1827      return cast<PointerLikeType>(getAtomicReductionLhsArg().getType());
1828    }
1829  }];
1830  let hasRegionVerifier = 1;
1831}
1832
1833//===----------------------------------------------------------------------===//
1834// [Spec 5.2] 10.5 masked Construct
1835//===----------------------------------------------------------------------===//
1836def MaskedOp : OpenMP_Op<"masked", clauses = [
1837    OpenMP_FilterClause
1838  ], singleRegion = 1> {
1839  let summary = "masked construct";
1840  let description = [{
1841    Masked construct allows to specify a structured block to be executed by a subset of
1842    threads of the current team.
1843  }] # clausesDescription;
1844
1845  let builders = [
1846    OpBuilder<(ins CArg<"const MaskedOperands &">:$clauses)>
1847  ];
1848}
1849
1850#endif // OPENMP_OPS
1851