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/IR/EnumAttr.td"
18include "mlir/IR/OpBase.td"
19include "mlir/Interfaces/SideEffectInterfaces.td"
20include "mlir/Interfaces/ControlFlowInterfaces.td"
21include "mlir/IR/SymbolInterfaces.td"
22include "mlir/Dialect/LLVMIR/LLVMOpBase.td"
23include "mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td"
24include "mlir/Dialect/OpenMP/OpenMPTypeInterfaces.td"
25
26def OpenMP_Dialect : Dialect {
27  let name = "omp";
28  let cppNamespace = "::mlir::omp";
29  let dependentDialects = ["::mlir::LLVM::LLVMDialect"];
30  let useDefaultAttributePrinterParser = 1;
31
32  // TODO: Flip to _Prefixed.
33  let emitAccessorPrefix = kEmitAccessorPrefix_Raw;
34}
35
36// OmpCommon requires definition of OpenACC_Dialect.
37include "mlir/Dialect/OpenMP/OmpCommon.td"
38
39class OpenMP_Op<string mnemonic, list<Trait> traits = []> :
40      Op<OpenMP_Dialect, mnemonic, traits>;
41
42// Type which can be constraint accepting standard integers and indices.
43def IntLikeType : AnyTypeOf<[AnyInteger, Index]>;
44
45def OpenMP_PointerLikeType : TypeAlias<OpenMP_PointerLikeTypeInterface,
46	"OpenMP-compatible variable type">;
47
48//===----------------------------------------------------------------------===//
49// 2.6 parallel Construct
50//===----------------------------------------------------------------------===//
51
52def ParallelOp : OpenMP_Op<"parallel", [
53                 AutomaticAllocationScope, AttrSizedOperandSegments,
54                 DeclareOpInterfaceMethods<OutlineableOpenMPOpInterface>,
55                 RecursiveSideEffects, ReductionClauseInterface]> {
56  let summary = "parallel construct";
57  let description = [{
58    The parallel construct includes a region of code which is to be executed
59    by a team of threads.
60
61    The optional $if_expr_var parameter specifies a boolean result of a
62    conditional check. If this value is 1 or is not provided then the parallel
63    region runs as normal, if it is 0 then the parallel region is executed with
64    one thread.
65
66    The optional $num_threads_var parameter specifies the number of threads which
67    should be used to execute the parallel region.
68
69    The $allocators_vars and $allocate_vars parameters are a variadic list of values
70    that specify the memory allocator to be used to obtain storage for private values.
71
72    Reductions can be performed in a parallel construct by specifying reduction
73    accumulator variables in `reduction_vars` and symbols referring to reduction
74    declarations in the `reductions` attribute. Each reduction is identified
75    by the accumulator it uses and accumulators must not be repeated in the same
76    reduction. The `omp.reduction` operation accepts the accumulator and a
77    partial value which is considered to be produced by the thread for the
78    given reduction. If multiple values are produced for the same accumulator,
79    i.e. there are multiple `omp.reduction`s, the last value is taken. The
80    reduction declaration specifies how to combine the values from each thread
81    into the final value, which is available in the accumulator after all the
82    threads complete.
83
84    The optional $proc_bind_val attribute controls the thread affinity for the execution
85    of the parallel region.
86  }];
87
88  let arguments = (ins Optional<I1>:$if_expr_var,
89             Optional<IntLikeType>:$num_threads_var,
90             Variadic<AnyType>:$allocate_vars,
91             Variadic<AnyType>:$allocators_vars,
92             Variadic<OpenMP_PointerLikeType>:$reduction_vars,
93             OptionalAttr<SymbolRefArrayAttr>:$reductions,
94             OptionalAttr<ProcBindKindAttr>:$proc_bind_val);
95
96  let regions = (region AnyRegion:$region);
97
98  let builders = [
99    OpBuilder<(ins CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>
100  ];
101  let assemblyFormat = [{
102    oilist( `reduction` `(`
103              custom<ReductionVarList>(
104                $reduction_vars, type($reduction_vars), $reductions
105              ) `)`
106          | `if` `(` $if_expr_var `:` type($if_expr_var) `)`
107          | `num_threads` `(` $num_threads_var `:` type($num_threads_var) `)`
108          | `allocate` `(`
109              custom<AllocateAndAllocator>(
110                $allocate_vars, type($allocate_vars),
111                $allocators_vars, type($allocators_vars)
112              ) `)`
113          | `proc_bind` `(` custom<ClauseAttr>($proc_bind_val) `)`
114    ) $region attr-dict
115  }];
116  let hasVerifier = 1;
117  let extraClassDeclaration = [{
118    // TODO: remove this once emitAccessorPrefix is set to
119    // kEmitAccessorPrefix_Prefixed for the dialect.
120    /// Returns the reduction variables
121    SmallVector<Value> getReductionVars() {
122      return SmallVector<Value>(reduction_vars().begin(),
123                                reduction_vars().end());
124    }
125  }];
126}
127
128def TerminatorOp : OpenMP_Op<"terminator", [Terminator]> {
129  let summary = "terminator for OpenMP regions";
130  let description = [{
131    A terminator operation for regions that appear in the body of OpenMP
132    operation.  These regions are not expected to return any value so the
133    terminator takes no operands. The terminator op returns control to the
134    enclosing op.
135  }];
136
137  let assemblyFormat = "attr-dict";
138}
139
140def OMP_ScheduleModNone         : I32EnumAttrCase<"none", 0>;
141def OMP_ScheduleModMonotonic    : I32EnumAttrCase<"monotonic", 1>;
142def OMP_ScheduleModNonmonotonic : I32EnumAttrCase<"nonmonotonic", 2>;
143// FIXME: remove this value for the modifier because this is handled using a
144// separate attribute
145def OMP_ScheduleModSIMD         : I32EnumAttrCase<"simd", 3>;
146
147def ScheduleModifier
148    : I32EnumAttr<"ScheduleModifier", "OpenMP Schedule Modifier",
149                  [OMP_ScheduleModNone, OMP_ScheduleModMonotonic,
150                   OMP_ScheduleModNonmonotonic, OMP_ScheduleModSIMD]> {
151  let genSpecializedAttr = 0;
152  let cppNamespace = "::mlir::omp";
153}
154def ScheduleModifierAttr : EnumAttr<OpenMP_Dialect, ScheduleModifier,
155                                    "sched_mod">;
156
157//===----------------------------------------------------------------------===//
158// 2.8.1 Sections Construct
159//===----------------------------------------------------------------------===//
160
161def SectionOp : OpenMP_Op<"section", [HasParent<"SectionsOp">]> {
162  let summary = "section directive";
163  let description = [{
164    A section operation encloses a region which represents one section in a
165    sections construct. A section op should always be surrounded by an
166    `omp.sections` operation.
167  }];
168  let regions = (region AnyRegion:$region);
169  let assemblyFormat = "$region attr-dict";
170}
171
172def SectionsOp : OpenMP_Op<"sections", [AttrSizedOperandSegments,
173                           ReductionClauseInterface]> {
174  let summary = "sections construct";
175  let description = [{
176    The sections construct is a non-iterative worksharing construct that
177    contains `omp.section` operations. The `omp.section` operations are to be
178    distributed among and executed by the threads in a team. Each `omp.section`
179    is executed once by one of the threads in the team in the context of its
180    implicit task.
181
182    Reductions can be performed in a sections construct by specifying reduction
183    accumulator variables in `reduction_vars` and symbols referring to reduction
184    declarations in the `reductions` attribute. Each reduction is identified
185    by the accumulator it uses and accumulators must not be repeated in the same
186    reduction. The `omp.reduction` operation accepts the accumulator and a
187    partial value which is considered to be produced by the section for the
188    given reduction. If multiple values are produced for the same accumulator,
189    i.e. there are multiple `omp.reduction`s, the last value is taken. The
190    reduction declaration specifies how to combine the values from each section
191    into the final value, which is available in the accumulator after all the
192    sections complete.
193
194    The $allocators_vars and $allocate_vars parameters are a variadic list of values
195    that specify the memory allocator to be used to obtain storage for private values.
196
197    The `nowait` attribute, when present, signifies that there should be no
198    implicit barrier at the end of the construct.
199  }];
200  let arguments = (ins Variadic<OpenMP_PointerLikeType>:$reduction_vars,
201                       OptionalAttr<SymbolRefArrayAttr>:$reductions,
202                       Variadic<AnyType>:$allocate_vars,
203                       Variadic<AnyType>:$allocators_vars,
204                       UnitAttr:$nowait);
205
206  let regions = (region SizedRegion<1>:$region);
207
208  let assemblyFormat = [{
209    oilist( `reduction` `(`
210              custom<ReductionVarList>(
211                $reduction_vars, type($reduction_vars), $reductions
212              ) `)`
213          | `allocate` `(`
214              custom<AllocateAndAllocator>(
215                $allocate_vars, type($allocate_vars),
216                $allocators_vars, type($allocators_vars)
217              ) `)`
218          | `nowait` $nowait
219    ) $region attr-dict
220  }];
221
222  let hasVerifier = 1;
223  let hasRegionVerifier = 1;
224
225  let extraClassDeclaration = [{
226    // TODO: remove this once emitAccessorPrefix is set to
227    // kEmitAccessorPrefix_Prefixed for the dialect.
228    /// Returns the reduction variables
229    SmallVector<Value> getReductionVars() {
230      return SmallVector<Value>(reduction_vars().begin(),
231                                reduction_vars().end());
232    }
233  }];
234}
235
236//===----------------------------------------------------------------------===//
237// 2.8.2 Single Construct
238//===----------------------------------------------------------------------===//
239
240def SingleOp : OpenMP_Op<"single", [AttrSizedOperandSegments]> {
241  let summary = "single directive";
242  let description = [{
243    The single construct specifies that the associated structured block is
244    executed by only one of the threads in the team (not necessarily the
245    master thread), in the context of its implicit task. The other threads
246    in the team, which do not execute the block, wait at an implicit barrier
247    at the end of the single construct unless a nowait clause is specified.
248  }];
249
250  let arguments = (ins Variadic<AnyType>:$allocate_vars,
251                       Variadic<AnyType>:$allocators_vars,
252                       UnitAttr:$nowait);
253
254  let regions = (region SizedRegion<1>:$region);
255
256  let assemblyFormat = [{
257    oilist(`allocate` `(`
258              custom<AllocateAndAllocator>(
259                $allocate_vars, type($allocate_vars),
260                $allocators_vars, type($allocators_vars)
261              ) `)`
262          |`nowait` $nowait
263    ) $region attr-dict
264  }];
265  let hasVerifier = 1;
266}
267
268//===----------------------------------------------------------------------===//
269// 2.9.2 Workshare Loop Construct
270//===----------------------------------------------------------------------===//
271
272def WsLoopOp : OpenMP_Op<"wsloop", [AttrSizedOperandSegments,
273                         AllTypesMatch<["lowerBound", "upperBound", "step"]>,
274                         RecursiveSideEffects, ReductionClauseInterface]> {
275  let summary = "worksharing-loop construct";
276  let description = [{
277    The worksharing-loop construct specifies that the iterations of the loop(s)
278    will be executed in parallel by threads in the current context. These
279    iterations are spread across threads that already exist in the enclosing
280    parallel region. The lower and upper bounds specify a half-open range: the
281    range includes the lower bound but does not include the upper bound. If the
282    `inclusive` attribute is specified then the upper bound is also included.
283
284    The body region can contain any number of blocks. The region is terminated
285    by "omp.yield" instruction without operands.
286
287    ```
288    omp.wsloop <clauses>
289    for (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
290      %a = load %arrA[%i1, %i2] : memref<?x?xf32>
291      %b = load %arrB[%i1, %i2] : memref<?x?xf32>
292      %sum = arith.addf %a, %b : f32
293      store %sum, %arrC[%i1, %i2] : memref<?x?xf32>
294      omp.yield
295    }
296    ```
297
298    The `linear_step_vars` operand additionally specifies the step for each
299    associated linear operand. Note that the `linear_vars` and
300    `linear_step_vars` variadic lists should contain the same number of
301    elements.
302
303    Reductions can be performed in a worksharing-loop by specifying reduction
304    accumulator variables in `reduction_vars` and symbols referring to reduction
305    declarations in the `reductions` attribute. Each reduction is identified
306    by the accumulator it uses and accumulators must not be repeated in the same
307    reduction. The `omp.reduction` operation accepts the accumulator and a
308    partial value which is considered to be produced by the current loop
309    iteration for the given reduction. If multiple values are produced for the
310    same accumulator, i.e. there are multiple `omp.reduction`s, the last value
311    is taken. The reduction declaration specifies how to combine the values from
312    each iteration into the final value, which is available in the accumulator
313    after the loop completes.
314
315    The optional `schedule_val` attribute specifies the loop schedule for this
316    loop, determining how the loop is distributed across the parallel threads.
317    The optional `schedule_chunk_var` associated with this determines further
318    controls this distribution.
319
320    Collapsed loops are represented by the worksharing-loop having a list of
321    indices, bounds and steps where the size of the list is equal to the
322    collapse value.
323
324    The `nowait` attribute, when present, signifies that there should be no
325    implicit barrier at the end of the loop.
326
327    The optional `ordered_val` attribute specifies how many loops are associated
328    with the worksharing-loop construct. The value of zero refers to the ordered
329    clause specified without parameter.
330
331    The optional `order` attribute specifies which order the iterations of the
332    associate loops are executed in. Currently the only option for this
333    attribute is "concurrent".
334  }];
335
336  let arguments = (ins Variadic<IntLikeType>:$lowerBound,
337             Variadic<IntLikeType>:$upperBound,
338             Variadic<IntLikeType>:$step,
339             Variadic<AnyType>:$linear_vars,
340             Variadic<I32>:$linear_step_vars,
341             Variadic<OpenMP_PointerLikeType>:$reduction_vars,
342             OptionalAttr<SymbolRefArrayAttr>:$reductions,
343             OptionalAttr<ScheduleKindAttr>:$schedule_val,
344             Optional<AnyType>:$schedule_chunk_var,
345             OptionalAttr<ScheduleModifierAttr>:$schedule_modifier,
346             UnitAttr:$simd_modifier,
347             UnitAttr:$nowait,
348             Confined<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$ordered_val,
349             OptionalAttr<OrderKindAttr>:$order_val,
350             UnitAttr:$inclusive);
351
352  let builders = [
353    OpBuilder<(ins "ValueRange":$lowerBound, "ValueRange":$upperBound,
354               "ValueRange":$step,
355               CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>,
356  ];
357
358  let regions = (region AnyRegion:$region);
359
360  let extraClassDeclaration = [{
361    /// Returns the number of loops in the worksharing-loop nest.
362    unsigned getNumLoops() { return lowerBound().size(); }
363
364    /// Returns the number of reduction variables.
365    unsigned getNumReductionVars() { return reduction_vars().size(); }
366
367    // TODO: remove this once emitAccessorPrefix is set to
368    // kEmitAccessorPrefix_Prefixed for the dialect.
369    /// Returns the reduction variables
370    SmallVector<Value> getReductionVars() {
371      return SmallVector<Value>(reduction_vars().begin(),
372                                reduction_vars().end());
373    }
374  }];
375  let hasCustomAssemblyFormat = 1;
376  let assemblyFormat = [{
377    oilist(`linear` `(`
378              custom<LinearClause>($linear_vars, type($linear_vars),
379                                   $linear_step_vars) `)`
380          |`schedule` `(`
381              custom<ScheduleClause>(
382                $schedule_val, $schedule_modifier, $simd_modifier,
383                $schedule_chunk_var, type($schedule_chunk_var)) `)`
384          |`nowait` $nowait
385          |`ordered` `(` $ordered_val `)`
386          |`order` `(` custom<ClauseAttr>($order_val) `)`
387          |`reduction` `(`
388              custom<ReductionVarList>(
389                $reduction_vars, type($reduction_vars), $reductions
390              ) `)`
391    ) `for` custom<LoopControl>($region, $lowerBound, $upperBound, $step,
392                                  type($step), $inclusive) attr-dict
393  }];
394  let hasVerifier = 1;
395}
396
397//===----------------------------------------------------------------------===//
398// Simd construct [2.9.3.1]
399//===----------------------------------------------------------------------===//
400
401def SimdLoopOp : OpenMP_Op<"simdloop", [AttrSizedOperandSegments,
402                         AllTypesMatch<["lowerBound", "upperBound", "step"]>]> {
403 let summary = "simd loop construct";
404  let description = [{
405    The simd construct can be applied to a loop to indicate that the loop can be
406    transformed into a SIMD loop (that is, multiple iterations of the loop can
407    be executed concurrently using SIMD instructions).. The lower and upper
408    bounds specify a half-open range: the range includes the lower bound but
409    does not include the upper bound. If the `inclusive` attribute is specified
410    then the upper bound is also included.
411
412    The body region can contain any number of blocks. The region is terminated
413    by "omp.yield" instruction without operands.
414
415    When an if clause is present and evaluates to false, the preferred number of
416    iterations to be executed concurrently is one, regardless of whether
417    a simdlen clause is specified.
418    ```
419    omp.simdloop <clauses>
420    for (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
421      // block operations
422      omp.yield
423    }
424    ```
425  }];
426
427  // TODO: Add other clauses
428  let arguments = (ins Variadic<IntLikeType>:$lowerBound,
429             Variadic<IntLikeType>:$upperBound,
430             Variadic<IntLikeType>:$step,
431             Optional<I1>:$if_expr,
432             UnitAttr:$inclusive
433     );
434
435  let regions = (region AnyRegion:$region);
436  let assemblyFormat = [{
437    oilist(`if` `(` $if_expr `)`
438    ) `for` custom<LoopControl>($region, $lowerBound, $upperBound, $step,
439                                  type($step), $inclusive) attr-dict
440  }];
441
442  let extraClassDeclaration = [{
443    /// Returns the number of loops in the simd loop nest.
444    unsigned getNumLoops() { return lowerBound().size(); }
445
446  }];
447
448  let hasCustomAssemblyFormat = 1;
449  let hasVerifier = 1;
450}
451
452
453def YieldOp : OpenMP_Op<"yield",
454    [NoSideEffect, ReturnLike, Terminator,
455     ParentOneOf<["WsLoopOp", "ReductionDeclareOp",
456     "AtomicUpdateOp", "SimdLoopOp"]>]> {
457  let summary = "loop yield and termination operation";
458  let description = [{
459    "omp.yield" yields SSA values from the OpenMP dialect op region and
460    terminates the region. The semantics of how the values are yielded is
461    defined by the parent operation.
462  }];
463
464  let arguments = (ins Variadic<AnyType>:$results);
465
466  let builders = [
467    OpBuilder<(ins), [{ build($_builder, $_state, {}); }]>
468  ];
469
470  let assemblyFormat = [{ ( `(` $results^ `:` type($results) `)` )? attr-dict}];
471}
472
473//===----------------------------------------------------------------------===//
474// 2.10.1 task Construct
475//===----------------------------------------------------------------------===//
476
477def TaskOp : OpenMP_Op<"task", [AttrSizedOperandSegments,
478                       OutlineableOpenMPOpInterface, AutomaticAllocationScope,
479                       ReductionClauseInterface]> {
480  let summary = "task construct";
481  let description = [{
482    The task construct defines an explicit task.
483
484    For definitions of "undeferred task", "included task", "final task" and
485    "mergeable task", please check OpenMP Specification.
486
487    When an `if` clause is present on a task construct, and the value of
488    `if_expr` evaluates to `false`, an "undeferred task" is generated, and the
489    encountering thread must suspend the current task region, for which
490    execution cannot be resumed until execution of the structured block that is
491    associated with the generated task is completed.
492
493    When a `final` clause is present on a task construct and the `final_expr`
494    evaluates to `true`, the generated task will be a "final task". All task
495    constructs encountered during execution of a final task will generate final
496    and included tasks.
497
498    If the `untied` clause is present on a task construct, any thread in the
499    team can resume the task region after a suspension. The `untied` clause is
500    ignored if a `final` clause is present on the same task construct and the
501    `final_expr` evaluates to `true`, or if a task is an included task.
502
503    When the `mergeable` clause is present on a task construct, the generated
504    task is a "mergeable task".
505
506    The `in_reduction` clause specifies that this particular task (among all the
507    tasks in current taskgroup, if any) participates in a reduction.
508
509    The `priority` clause is a hint for the priority of the generated task.
510    The `priority` is a non-negative integer expression that provides a hint for
511    task execution order. Among all tasks ready to be executed, higher priority
512    tasks (those with a higher numerical value in the priority clause
513    expression) are recommended to execute before lower priority ones. The
514    default priority-value when no priority clause is specified should be
515    assumed to be zero (the lowest priority).
516
517    The `allocators_vars` and `allocate_vars` arguments are a variadic list of
518    values that specify the memory allocator to be used to obtain storage for
519    private values.
520
521  }];
522
523  // TODO: depend, affinity and detach clauses
524  let arguments = (ins Optional<I1>:$if_expr,
525                       Optional<I1>:$final_expr,
526                       UnitAttr:$untied,
527                       UnitAttr:$mergeable,
528                       Variadic<OpenMP_PointerLikeType>:$in_reduction_vars,
529                       OptionalAttr<SymbolRefArrayAttr>:$in_reductions,
530                       Optional<I32>:$priority,
531                       Variadic<AnyType>:$allocate_vars,
532                       Variadic<AnyType>:$allocators_vars);
533  let regions = (region AnyRegion:$region);
534  let assemblyFormat = [{
535    oilist(`if` `(` $if_expr `)`
536          |`final` `(` $final_expr `)`
537          |`untied` $untied
538          |`mergeable` $mergeable
539          |`in_reduction` `(`
540              custom<ReductionVarList>(
541                $in_reduction_vars, type($in_reduction_vars), $in_reductions
542              ) `)`
543          |`priority` `(` $priority `)`
544          |`allocate` `(`
545              custom<AllocateAndAllocator>(
546                $allocate_vars, type($allocate_vars),
547                $allocators_vars, type($allocators_vars)
548              ) `)`
549    ) $region attr-dict
550  }];
551  let extraClassDeclaration = [{
552    /// Returns the reduction variables
553    SmallVector<Value> getReductionVars() {
554      return SmallVector<Value>(in_reduction_vars().begin(),
555                                in_reduction_vars().end());
556    }
557  }];
558  let hasVerifier = 1;
559}
560
561def TaskLoopOp : OpenMP_Op<"taskloop", [AttrSizedOperandSegments,
562                           AutomaticAllocationScope, RecursiveSideEffects,
563                           AllTypesMatch<["lowerBound", "upperBound", "step"]>,
564                           ReductionClauseInterface]> {
565  let summary = "taskloop construct";
566  let description = [{
567    The taskloop construct specifies that the iterations of one or more
568    associated loops will be executed in parallel using explicit tasks. The
569    iterations are distributed across tasks generated by the construct and
570    scheduled to be executed.
571
572    The `lowerBound` and `upperBound` specify a half-open range: the range
573    includes the lower bound but does not include the upper bound. If the
574    `inclusive` attribute is specified then the upper bound is also included.
575    The `step` specifies the loop step.
576
577    The body region can contain any number of blocks.
578
579    ```
580    omp.taskloop <clauses>
581    for (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
582      %a = load %arrA[%i1, %i2] : memref<?x?xf32>
583      %b = load %arrB[%i1, %i2] : memref<?x?xf32>
584      %sum = arith.addf %a, %b : f32
585      store %sum, %arrC[%i1, %i2] : memref<?x?xf32>
586      omp.terminator
587    }
588    ```
589
590    For definitions of "undeferred task", "included task", "final task" and
591    "mergeable task", please check OpenMP Specification.
592
593    When an `if` clause is present on a taskloop construct, and if the `if`
594    clause expression evaluates to `false`, undeferred tasks are generated. The
595    use of a variable in an `if` clause expression of a taskloop construct
596    causes an implicit reference to the variable in all enclosing constructs.
597
598    When a `final` clause is present on a taskloop construct and the `final`
599    clause expression evaluates to `true`, the generated tasks will be final
600    tasks. The use of a variable in a `final` clause expression of a taskloop
601    construct causes an implicit reference to the variable in all enclosing
602    constructs.
603
604    If the `untied` clause is specified, all tasks generated by the taskloop
605    construct are untied tasks.
606
607    When the `mergeable` clause is present on a taskloop construct, each
608    generated task is a mergeable task.
609
610    Reductions can be performed in a loop by specifying reduction accumulator
611    variables in `reduction_vars` or `in_reduction_vars` and symbols referring
612    to reduction declarations in the `reductions` or `in_reductions` attribute.
613    Each reduction is identified by the accumulator it uses and accumulators
614    must not be repeated in the same reduction. The `omp.reduction` operation
615    accepts the accumulator and a partial value which is considered to be
616    produced by the current loop iteration for the given reduction. If multiple
617    values are produced for the same accumulator, i.e. there are multiple
618    `omp.reduction`s, the last value is taken. The reduction declaration
619    specifies how to combine the values from each iteration into the final
620    value, which is available in the accumulator after the loop completes.
621
622    If an `in_reduction` clause is present on the taskloop construct, the
623    behavior is as if each generated task was defined by a task construct on
624    which an `in_reduction` clause with the same reduction operator and list
625    items is present. Thus, the generated tasks are participants of a reduction
626    previously defined by a reduction scoping clause.
627
628    If a `reduction` clause is present on the taskloop construct, the behavior
629    is as if a `task_reduction` clause with the same reduction operator and list
630    items was applied to the implicit taskgroup construct enclosing the taskloop
631    construct. The taskloop construct executes as if each generated task was
632    defined by a task construct on which an `in_reduction` clause with the same
633    reduction operator and list items is present. Thus, the generated tasks are
634    participants of the reduction defined by the `task_reduction` clause that
635    was applied to the implicit taskgroup construct.
636
637    When a `priority` clause is present on a taskloop construct, the generated
638    tasks use the `priority-value` as if it was specified for each individual
639    task. If the `priority` clause is not specified, tasks generated by the
640    taskloop construct have the default task priority (zero).
641
642    The `allocators_vars` and `allocate_vars` arguments are a variadic list of
643    values that specify the memory allocator to be used to obtain storage for
644    private values.
645
646    If a `grainsize` clause is present on the taskloop construct, the number of
647    logical loop iterations assigned to each generated task is greater than or
648    equal to the minimum of the value of the grain-size expression and the
649    number of logical loop iterations, but less than two times the value of the
650    grain-size expression.
651
652    If `num_tasks` is specified, the taskloop construct creates as many tasks as
653    the minimum of the num-tasks expression and the number of logical loop
654    iterations. Each task must have at least one logical loop iteration.
655
656    By default, the taskloop construct executes as if it was enclosed in a
657    taskgroup construct with no statements or directives outside of the taskloop
658    construct. Thus, the taskloop construct creates an implicit taskgroup
659    region. If the `nogroup` clause is present, no implicit taskgroup region is
660    created.
661  }];
662
663  let arguments = (ins Variadic<IntLikeType>:$lowerBound,
664                       Variadic<IntLikeType>:$upperBound,
665                       Variadic<IntLikeType>:$step,
666                       UnitAttr:$inclusive,
667                       Optional<I1>:$if_expr,
668                       Optional<I1>:$final_expr,
669                       UnitAttr:$untied,
670                       UnitAttr:$mergeable,
671                       Variadic<OpenMP_PointerLikeType>:$in_reduction_vars,
672                       OptionalAttr<SymbolRefArrayAttr>:$in_reductions,
673                       Variadic<OpenMP_PointerLikeType>:$reduction_vars,
674                       OptionalAttr<SymbolRefArrayAttr>:$reductions,
675                       Optional<IntLikeType>:$priority,
676                       Variadic<AnyType>:$allocate_vars,
677                       Variadic<AnyType>:$allocators_vars,
678                       Optional<IntLikeType>: $grain_size,
679                       Optional<IntLikeType>: $num_tasks,
680                       UnitAttr: $nogroup);
681
682  let regions = (region AnyRegion:$region);
683
684  let assemblyFormat = [{
685    oilist(`if` `(` $if_expr `)`
686          |`final` `(` $final_expr `)`
687          |`untied` $untied
688          |`mergeable` $mergeable
689          |`in_reduction` `(`
690              custom<ReductionVarList>(
691                $in_reduction_vars, type($in_reduction_vars), $in_reductions
692              ) `)`
693          |`reduction` `(`
694              custom<ReductionVarList>(
695                $reduction_vars, type($reduction_vars), $reductions
696              ) `)`
697          |`priority` `(` $priority `:` type($priority) `)`
698          |`allocate` `(`
699              custom<AllocateAndAllocator>(
700                $allocate_vars, type($allocate_vars),
701                $allocators_vars, type($allocators_vars)
702              ) `)`
703          |`grain_size` `(` $grain_size `:` type($grain_size) `)`
704          |`num_tasks` `(` $num_tasks `:` type($num_tasks) `)`
705          |`nogroup` $nogroup
706    ) `for` custom<LoopControl>($region, $lowerBound, $upperBound, $step,
707                                  type($step), $inclusive) attr-dict
708  }];
709
710  let extraClassDeclaration = [{
711    /// Returns the reduction variables
712    SmallVector<Value> getReductionVars();
713    void getEffects(SmallVectorImpl<MemoryEffects::EffectInstance> &effects);
714  }];
715
716  let hasVerifier = 1;
717}
718
719def TaskGroupOp : OpenMP_Op<"taskgroup", [AttrSizedOperandSegments,
720                            ReductionClauseInterface,
721                            AutomaticAllocationScope]> {
722  let summary = "taskgroup construct";
723  let description = [{
724    The taskgroup construct specifies a wait on completion of child tasks of the
725    current task and their descendent tasks.
726
727    When a thread encounters a taskgroup construct, it starts executing the
728    region. All child tasks generated in the taskgroup region and all of their
729    descendants that bind to the same parallel region as the taskgroup region
730    are part of the taskgroup set associated with the taskgroup region. There is
731    an implicit task scheduling point at the end of the taskgroup region. The
732    current task is suspended at the task scheduling point until all tasks in
733    the taskgroup set complete execution.
734
735    The `task_reduction` clause specifies a reduction among tasks. For each list
736    item, the number of copies is unspecified. Any copies associated with the
737    reduction are initialized before they are accessed by the tasks
738    participating in the reduction. After the end of the region, the original
739    list item contains the result of the reduction.
740
741    The `allocators_vars` and `allocate_vars` arguments are a variadic list of
742    values that specify the memory allocator to be used to obtain storage for
743    private values.
744  }];
745
746  let arguments = (ins Variadic<OpenMP_PointerLikeType>:$task_reduction_vars,
747                       OptionalAttr<SymbolRefArrayAttr>:$task_reductions,
748                       Variadic<AnyType>:$allocate_vars,
749                       Variadic<AnyType>:$allocators_vars);
750
751  let regions = (region AnyRegion:$region);
752
753  let assemblyFormat = [{
754    oilist(`task_reduction` `(`
755              custom<ReductionVarList>(
756                $task_reduction_vars, type($task_reduction_vars), $task_reductions
757              ) `)`
758          |`allocate` `(`
759              custom<AllocateAndAllocator>(
760                $allocate_vars, type($allocate_vars),
761                $allocators_vars, type($allocators_vars)
762              ) `)`
763    ) $region attr-dict
764  }];
765
766  let extraClassDeclaration = [{
767    /// Returns the reduction variables
768    operand_range getReductionVars() { return task_reduction_vars(); }
769  }];
770
771  let hasVerifier = 1;
772
773}
774
775//===----------------------------------------------------------------------===//
776// 2.10.4 taskyield Construct
777//===----------------------------------------------------------------------===//
778
779def TaskyieldOp : OpenMP_Op<"taskyield"> {
780  let summary = "taskyield construct";
781  let description = [{
782    The taskyield construct specifies that the current task can be suspended
783    in favor of execution of a different task.
784  }];
785
786  let assemblyFormat = "attr-dict";
787}
788
789//===----------------------------------------------------------------------===//
790// 2.13.7 flush Construct
791//===----------------------------------------------------------------------===//
792def FlushOp : OpenMP_Op<"flush"> {
793  let summary = "flush construct";
794  let description = [{
795    The flush construct executes the OpenMP flush operation. This operation
796    makes a thread’s temporary view of memory consistent with memory and
797    enforces an order on the memory operations of the variables explicitly
798    specified or implied.
799  }];
800
801  let arguments = (ins Variadic<OpenMP_PointerLikeType>:$varList);
802
803  let assemblyFormat = [{ ( `(` $varList^ `:` type($varList) `)` )? attr-dict}];
804  let extraClassDeclaration = [{
805    /// The number of variable operands.
806    unsigned getNumVariableOperands() {
807      return getOperation()->getNumOperands();
808    }
809    /// The i-th variable operand passed.
810    Value getVariableOperand(unsigned i) {
811      return getOperand(i);
812    }
813  }];
814}
815//===----------------------------------------------------------------------===//
816// 2.14.5 target construct
817//===----------------------------------------------------------------------===//
818
819def TargetOp : OpenMP_Op<"target",[AttrSizedOperandSegments]> {
820  let summary = "target construct";
821  let description = [{
822    The target construct includes a region of code which is to be executed
823    on a device.
824
825    The optional $if_expr parameter specifies a boolean result of a
826    conditional check. If this value is 1 or is not provided then the target
827    region runs on a device, if it is 0 then the target region is executed on the
828    host device.
829
830    The optional $device parameter specifies the device number for the target region.
831
832    The optional $thread_limit specifies the limit on the number of threads
833
834    The optional $nowait elliminates the implicit barrier so the parent task can make progress
835    even if the target task is not yet completed.
836
837    TODO:  map, is_device_ptr, depend, defaultmap, in_reduction
838
839  }];
840
841  let arguments = (ins Optional<I1>:$if_expr,
842                       Optional<AnyInteger>:$device,
843                       Optional<AnyInteger>:$thread_limit,
844                       UnitAttr:$nowait);
845
846  let regions = (region AnyRegion:$region);
847
848  let assemblyFormat = [{
849    oilist( `if` `(` $if_expr `)`
850          | `device` `(` $device `:` type($device) `)`
851          | `thread_limit` `(` $thread_limit `:` type($thread_limit) `)`
852          | `nowait` $nowait
853    ) $region attr-dict
854  }];
855}
856
857
858//===----------------------------------------------------------------------===//
859// 2.16 master Construct
860//===----------------------------------------------------------------------===//
861def MasterOp : OpenMP_Op<"master"> {
862  let summary = "master construct";
863  let description = [{
864    The master construct specifies a structured block that is executed by
865    the master thread of the team.
866  }];
867
868  let regions = (region AnyRegion:$region);
869
870  let assemblyFormat = "$region attr-dict";
871}
872
873//===----------------------------------------------------------------------===//
874// 2.17.1 critical Construct
875//===----------------------------------------------------------------------===//
876def CriticalDeclareOp : OpenMP_Op<"critical.declare", [Symbol]> {
877  let summary = "declares a named critical section.";
878
879  let description = [{
880    Declares a named critical section.
881
882    The name can be used in critical constructs in the dialect.
883  }];
884
885  let arguments = (ins SymbolNameAttr:$sym_name,
886                       DefaultValuedAttr<I64Attr, "0">:$hint_val);
887
888  let assemblyFormat = [{
889    $sym_name oilist(`hint` `(` custom<SynchronizationHint>($hint_val) `)`)
890    attr-dict
891  }];
892  let hasVerifier = 1;
893}
894
895
896def CriticalOp : OpenMP_Op<"critical",
897    [DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
898  let summary = "critical construct";
899  let description = [{
900    The critical construct imposes a restriction on the associated structured
901    block (region) to be executed by only a single thread at a time.
902  }];
903
904  let arguments = (ins OptionalAttr<FlatSymbolRefAttr>:$name);
905
906  let regions = (region AnyRegion:$region);
907
908  let assemblyFormat = [{
909    (`(` $name^ `)`)? $region attr-dict
910  }];
911}
912
913//===----------------------------------------------------------------------===//
914// 2.17.2 barrier Construct
915//===----------------------------------------------------------------------===//
916
917def BarrierOp : OpenMP_Op<"barrier"> {
918  let summary = "barrier construct";
919  let description = [{
920    The barrier construct specifies an explicit barrier at the point at which
921    the construct appears.
922  }];
923
924  let assemblyFormat = "attr-dict";
925}
926
927//===----------------------------------------------------------------------===//
928// [5.1] 2.19.9 ordered Construct
929//===----------------------------------------------------------------------===//
930
931def ClauseDependSource : I32EnumAttrCase<"dependsource", 0>;
932def ClauseDependSink   : I32EnumAttrCase<"dependsink",   1>;
933
934def ClauseDepend : I32EnumAttr<
935    "ClauseDepend",
936    "depend clause",
937    [ClauseDependSource, ClauseDependSink]> {
938  let genSpecializedAttr = 0;
939  let cppNamespace = "::mlir::omp";
940}
941def ClauseDependAttr : EnumAttr<OpenMP_Dialect, ClauseDepend, "clause_depend"> {
942  let assemblyFormat = "`(` $value `)`";
943}
944
945def OrderedOp : OpenMP_Op<"ordered"> {
946  let summary = "ordered construct without region";
947  let description = [{
948    The ordered construct without region is a stand-alone directive that
949    specifies cross-iteration dependences in a doacross loop nest.
950
951    The `depend_type_val` attribute refers to either the DEPEND(SOURCE) clause
952    or the DEPEND(SINK: vec) clause.
953
954    The `num_loops_val` attribute specifies the number of loops in the doacross
955    nest.
956
957    The `depend_vec_vars` is a variadic list of operands that specifies the index
958    of the loop iterator in the doacross nest for the DEPEND(SOURCE) clause or
959    the index of the element of "vec" for the DEPEND(SINK: vec) clause. It
960    contains the operands in multiple "vec" when multiple DEPEND(SINK: vec)
961    clauses exist in one ORDERED directive.
962  }];
963
964  let arguments = (ins OptionalAttr<ClauseDependAttr>:$depend_type_val,
965             Confined<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$num_loops_val,
966             Variadic<AnyType>:$depend_vec_vars);
967
968  let assemblyFormat = [{
969    ( `depend_type` `` $depend_type_val^ )?
970    ( `depend_vec` `(` $depend_vec_vars^ `:` type($depend_vec_vars) `)` )?
971    attr-dict
972  }];
973  let hasVerifier = 1;
974}
975
976def OrderedRegionOp : OpenMP_Op<"ordered_region"> {
977  let summary = "ordered construct with region";
978  let description = [{
979    The ordered construct with region specifies a structured block in a
980    worksharing-loop, SIMD, or worksharing-loop SIMD region that is executed in
981    the order of the loop iterations.
982
983    The `simd` attribute corresponds to the SIMD clause specified. If it is not
984    present, it behaves as if the THREADS clause is specified or no clause is
985    specified.
986  }];
987
988  let arguments = (ins UnitAttr:$simd);
989
990  let regions = (region AnyRegion:$region);
991
992  let assemblyFormat = [{ ( `simd` $simd^ )? $region attr-dict}];
993  let hasVerifier = 1;
994}
995
996//===----------------------------------------------------------------------===//
997// 2.17.5 taskwait Construct
998//===----------------------------------------------------------------------===//
999
1000def TaskwaitOp : OpenMP_Op<"taskwait"> {
1001  let summary = "taskwait construct";
1002  let description = [{
1003    The taskwait construct specifies a wait on the completion of child tasks
1004    of the current task.
1005  }];
1006
1007  let assemblyFormat = "attr-dict";
1008}
1009
1010//===----------------------------------------------------------------------===//
1011// 2.17.7 atomic construct
1012//===----------------------------------------------------------------------===//
1013
1014// In the OpenMP Specification, atomic construct has an `atomic-clause` which
1015// can take the values `read`, `write`, `update` and `capture`. These four
1016// kinds of atomic constructs are fundamentally independent and are handled
1017// separately while lowering. Having four separate operations (one for each
1018// value of the clause) here decomposes handling of this construct into a
1019// two-step process.
1020
1021def AtomicReadOp : OpenMP_Op<"atomic.read", [AllTypesMatch<["x", "v"]>]> {
1022
1023  let summary = "performs an atomic read";
1024
1025  let description = [{
1026    This operation performs an atomic read.
1027
1028    The operand `x` is the address from where the value is atomically read.
1029    The operand `v` is the address where the value is stored after reading.
1030
1031    `hint` is the value of hint (as specified in the hint clause). It is a
1032    compile time constant. As the name suggests, this is just a hint for
1033    optimization.
1034
1035    `memory_order` indicates the memory ordering behavior of the construct. It
1036    can be one of `seq_cst`, `acquire` or `relaxed`.
1037  }];
1038
1039  let arguments = (ins OpenMP_PointerLikeType:$x,
1040                       OpenMP_PointerLikeType:$v,
1041                       DefaultValuedAttr<I64Attr, "0">:$hint_val,
1042                       OptionalAttr<MemoryOrderKindAttr>:$memory_order_val);
1043  let assemblyFormat = [{
1044    $v `=` $x
1045    oilist( `memory_order` `(` custom<ClauseAttr>($memory_order_val) `)`
1046          | `hint` `(` custom<SynchronizationHint>($hint_val) `)`)
1047    `:` type($x) attr-dict
1048  }];
1049  let hasVerifier = 1;
1050  let extraClassDeclaration = [{
1051    /// The number of variable operands.
1052    unsigned getNumVariableOperands() {
1053      assert(x() && "expected 'x' operand");
1054      assert(v() && "expected 'v' operand");
1055      return 2;
1056    }
1057
1058    /// The i-th variable operand passed.
1059    Value getVariableOperand(unsigned i) {
1060      assert(i < 2 && "invalid index position for an operand");
1061      return i == 0 ? x() : v();
1062    }
1063  }];
1064}
1065
1066def AtomicWriteOp : OpenMP_Op<"atomic.write"> {
1067
1068  let summary = "performs an atomic write";
1069
1070  let description = [{
1071    This operation performs an atomic write.
1072
1073    The operand `address` is the address to where the `value` is atomically
1074    written w.r.t. multiple threads. The evaluation of `value` need not be
1075    atomic w.r.t. the write to address. In general, the type(address) must
1076    dereference to type(value).
1077
1078    `hint` is the value of hint (as specified in the hint clause). It is a
1079    compile time constant. As the name suggests, this is just a hint for
1080    optimization.
1081
1082    `memory_order` indicates the memory ordering behavior of the construct. It
1083    can be one of `seq_cst`, `release` or `relaxed`.
1084  }];
1085
1086  let arguments = (ins OpenMP_PointerLikeType:$address,
1087                       AnyType:$value,
1088                       DefaultValuedAttr<I64Attr, "0">:$hint_val,
1089                       OptionalAttr<MemoryOrderKindAttr>:$memory_order_val);
1090  let assemblyFormat = [{
1091    $address `=` $value
1092    oilist( `hint` `(` custom<SynchronizationHint>($hint_val) `)`
1093          | `memory_order` `(` custom<ClauseAttr>($memory_order_val) `)`)
1094    `:` type($address) `,` type($value)
1095    attr-dict
1096  }];
1097  let hasVerifier = 1;
1098  let extraClassDeclaration = [{
1099    /// The number of variable operands.
1100    unsigned getNumVariableOperands() {
1101      assert(address() && "expected address operand");
1102      assert(value() && "expected value operand");
1103      return 2;
1104    }
1105
1106    /// The i-th variable operand passed.
1107    Value getVariableOperand(unsigned i) {
1108      assert(i < 2 && "invalid index position for an operand");
1109      return i == 0 ? address() : value();
1110    }
1111  }];
1112}
1113
1114def AtomicUpdateOp : OpenMP_Op<"atomic.update",
1115                               [SingleBlockImplicitTerminator<"YieldOp">]> {
1116
1117  let summary = "performs an atomic update";
1118
1119  let description = [{
1120    This operation performs an atomic update.
1121
1122    The operand `x` is exactly the same as the operand `x` in the OpenMP
1123    Standard (OpenMP 5.0, section 2.17.7). It is the address of the variable
1124    that is being updated. `x` is atomically read/written.
1125
1126    `hint` is the value of hint (as used in the hint clause). It is a compile
1127    time constant. As the name suggests, this is just a hint for optimization.
1128
1129    `memory_order` indicates the memory ordering behavior of the construct. It
1130    can be one of `seq_cst`, `release` or `relaxed`.
1131
1132    The region describes how to update the value of `x`. It takes the value at
1133    `x` as an input and must yield the updated value. Only the update to `x` is
1134    atomic. Generally the region must have only one instruction, but can
1135    potentially have more than one instructions too. The update is sematically
1136    similar to a compare-exchange loop based atomic update.
1137
1138    The syntax of atomic update operation is different from atomic read and
1139    atomic write operations. This is because only the host dialect knows how to
1140    appropriately update a value. For example, while generating LLVM IR, if
1141    there are no special `atomicrmw` instructions for the operation-type
1142    combination in atomic update, a compare-exchange loop is generated, where
1143    the core update operation is directly translated like regular operations by
1144    the host dialect. The front-end must handle semantic checks for allowed
1145    operations.
1146  }];
1147
1148  let arguments = (ins OpenMP_PointerLikeType:$x,
1149                       DefaultValuedAttr<I64Attr, "0">:$hint_val,
1150                       OptionalAttr<MemoryOrderKindAttr>:$memory_order_val);
1151  let regions = (region SizedRegion<1>:$region);
1152  let assemblyFormat = [{
1153    oilist( `memory_order` `(` custom<ClauseAttr>($memory_order_val) `)`
1154          | `hint` `(` custom<SynchronizationHint>($hint_val) `)`)
1155    $x `:` type($x) $region attr-dict
1156  }];
1157  let hasVerifier = 1;
1158  let hasRegionVerifier = 1;
1159  let extraClassDeclaration = [{
1160    Operation* getFirstOp() {
1161      return &getRegion().front().getOperations().front();
1162    }
1163  }];
1164}
1165
1166def AtomicCaptureOp : OpenMP_Op<"atomic.capture",
1167    [SingleBlockImplicitTerminator<"TerminatorOp">]> {
1168  let summary = "performs an atomic capture";
1169  let description = [{
1170    This operation performs an atomic capture.
1171
1172    `hint` is the value of hint (as used in the hint clause). It is a compile
1173    time constant. As the name suggests, this is just a hint for optimization.
1174
1175    `memory_order` indicates the memory ordering behavior of the construct. It
1176    can be one of `seq_cst`, `acq_rel`, `release`, `acquire` or `relaxed`.
1177
1178    The region has the following allowed forms:
1179
1180    ```
1181      omp.atomic.capture {
1182        omp.atomic.update ...
1183        omp.atomic.read ...
1184        omp.terminator
1185      }
1186
1187      omp.atomic.capture {
1188        omp.atomic.read ...
1189        omp.atomic.update ...
1190        omp.terminator
1191      }
1192
1193      omp.atomic.capture {
1194        omp.atomic.read ...
1195        omp.atomic.write ...
1196        omp.terminator
1197      }
1198    ```
1199
1200  }];
1201
1202  let arguments = (ins DefaultValuedAttr<I64Attr, "0">:$hint_val,
1203                       OptionalAttr<MemoryOrderKindAttr>:$memory_order_val);
1204  let regions = (region SizedRegion<1>:$region);
1205  let assemblyFormat = [{
1206    oilist(`memory_order` `(` custom<ClauseAttr>($memory_order_val) `)`
1207          |`hint` `(` custom<SynchronizationHint>($hint_val) `)`)
1208    $region attr-dict
1209  }];
1210  let hasRegionVerifier = 1;
1211  let hasVerifier = 1;
1212  let extraClassDeclaration = [{
1213    /// Returns the first operation in atomic capture region
1214    Operation* getFirstOp();
1215
1216    /// Returns the second operation in atomic capture region
1217    Operation* getSecondOp();
1218
1219    /// Returns the `atomic.read` operation inside the region, if any.
1220    /// Otherwise, it returns nullptr.
1221    AtomicReadOp getAtomicReadOp();
1222
1223    /// Returns the `atomic.write` operation inside the region, if any.
1224    /// Otherwise, it returns nullptr.
1225    AtomicWriteOp getAtomicWriteOp();
1226
1227    /// Returns the `atomic.update` operation inside the region, if any.
1228    /// Otherwise, it returns nullptr.
1229    AtomicUpdateOp getAtomicUpdateOp();
1230  }];
1231}
1232
1233//===----------------------------------------------------------------------===//
1234// [5.1] 2.21.2 threadprivate Directive
1235//===----------------------------------------------------------------------===//
1236
1237def ThreadprivateOp : OpenMP_Op<"threadprivate",
1238                                [AllTypesMatch<["sym_addr", "tls_addr"]>]> {
1239  let summary = "threadprivate directive";
1240  let description = [{
1241    The threadprivate directive specifies that variables are replicated, with
1242    each thread having its own copy.
1243
1244    The current implementation uses the OpenMP runtime to provide thread-local
1245    storage (TLS). Using the TLS feature of the LLVM IR will be supported in
1246    future.
1247
1248    This operation takes in the address of a symbol that represents the original
1249    variable and returns the address of its TLS. All occurrences of
1250    threadprivate variables in a parallel region should use the TLS returned by
1251    this operation.
1252
1253    The `sym_addr` refers to the address of the symbol, which is a pointer to
1254    the original variable.
1255  }];
1256
1257  let arguments = (ins OpenMP_PointerLikeType:$sym_addr);
1258  let results = (outs OpenMP_PointerLikeType:$tls_addr);
1259  let assemblyFormat = [{
1260    $sym_addr `:` type($sym_addr) `->` type($tls_addr) attr-dict
1261  }];
1262  let extraClassDeclaration = [{
1263    /// The number of variable operands.
1264    unsigned getNumVariableOperands() {
1265      assert(sym_addr() && "expected one variable operand");
1266      return 1;
1267    }
1268
1269    /// The i-th variable operand passed.
1270    Value getVariableOperand(unsigned i) {
1271      assert(i == 0 && "invalid index position for an operand");
1272      return sym_addr();
1273    }
1274  }];
1275}
1276
1277//===----------------------------------------------------------------------===//
1278// 2.18.1 Cancel Construct
1279//===----------------------------------------------------------------------===//
1280def CancelOp : OpenMP_Op<"cancel"> {
1281  let summary = "cancel directive";
1282  let description = [{
1283    The cancel construct activates cancellation of the innermost enclosing
1284    region of the type specified.
1285  }];
1286  let arguments = (ins CancellationConstructTypeAttr:$cancellation_construct_type_val,
1287                       Optional<I1>:$if_expr);
1288  let assemblyFormat = [{ `cancellation_construct_type` `(`
1289                          custom<ClauseAttr>($cancellation_construct_type_val) `)`
1290                          ( `if` `(` $if_expr^ `)` )? attr-dict}];
1291  let hasVerifier = 1;
1292}
1293
1294//===----------------------------------------------------------------------===//
1295// 2.18.2 Cancellation Point Construct
1296//===----------------------------------------------------------------------===//
1297def CancellationPointOp : OpenMP_Op<"cancellationpoint"> {
1298  let summary = "cancellation point directive";
1299  let description = [{
1300    The cancellation point construct introduces a user-defined cancellation
1301    point at which implicit or explicit tasks check if cancellation of the
1302    innermost enclosing region of the type specified has been activated.
1303  }];
1304  let arguments = (ins CancellationConstructTypeAttr:$cancellation_construct_type_val);
1305  let assemblyFormat = [{ `cancellation_construct_type` `(`
1306                           custom<ClauseAttr>($cancellation_construct_type_val) `)`
1307                           attr-dict}];
1308  let hasVerifier = 1;
1309}
1310
1311//===----------------------------------------------------------------------===//
1312// 2.19.5.7 declare reduction Directive
1313//===----------------------------------------------------------------------===//
1314
1315def ReductionDeclareOp : OpenMP_Op<"reduction.declare", [Symbol,
1316                                                         IsolatedFromAbove]> {
1317  let summary = "declares a reduction kind";
1318
1319  let description = [{
1320    Declares an OpenMP reduction kind. This requires two mandatory and one
1321    optional region.
1322
1323      1. The initializer region specifies how to initialize the thread-local
1324         reduction value. This is usually the neutral element of the reduction.
1325         For convenience, the region has an argument that contains the value
1326         of the reduction accumulator at the start of the reduction. It is
1327         expected to `omp.yield` the new value on all control flow paths.
1328      2. The reduction region specifies how to combine two values into one, i.e.
1329         the reduction operator. It accepts the two values as arguments and is
1330         expected to `omp.yield` the combined value on all control flow paths.
1331      3. The atomic reduction region is optional and specifies how two values
1332         can be combined atomically given local accumulator variables. It is
1333         expected to store the combined value in the first accumulator variable.
1334
1335    Note that the MLIR type system does not allow for type-polymorphic
1336    reductions. Separate reduction declarations should be created for different
1337    element and accumulator types.
1338
1339    For initializer and reduction regions, the operand to `omp.yield` must
1340    match the parent operation's results.
1341  }];
1342
1343  let arguments = (ins SymbolNameAttr:$sym_name,
1344                       TypeAttr:$type);
1345
1346  let regions = (region AnyRegion:$initializerRegion,
1347                        AnyRegion:$reductionRegion,
1348                        AnyRegion:$atomicReductionRegion);
1349
1350  let assemblyFormat = "$sym_name `:` $type attr-dict-with-keyword "
1351                       "`init` $initializerRegion "
1352                       "`combiner` $reductionRegion "
1353                       "custom<AtomicReductionRegion>($atomicReductionRegion)";
1354
1355  let extraClassDeclaration = [{
1356    PointerLikeType getAccumulatorType() {
1357      if (atomicReductionRegion().empty())
1358        return {};
1359
1360      return atomicReductionRegion().front().getArgument(0).getType();
1361    }
1362  }];
1363  let hasRegionVerifier = 1;
1364}
1365
1366//===----------------------------------------------------------------------===//
1367// 2.19.5.4 reduction clause
1368//===----------------------------------------------------------------------===//
1369
1370def ReductionOp : OpenMP_Op<"reduction", [
1371    TypesMatchWith<"value types matches accumulator element type",
1372                   "accumulator", "operand",
1373                 "$_self.cast<::mlir::omp::PointerLikeType>().getElementType()">
1374  ]> {
1375  let summary = "reduction construct";
1376  let description = [{
1377    Indicates the value that is produced by the current reduction-participating
1378    entity for a reduction requested in some ancestor. The reduction is
1379    identified by the accumulator, but the value of the accumulator may not be
1380    updated immediately.
1381  }];
1382
1383  let arguments= (ins AnyType:$operand, OpenMP_PointerLikeType:$accumulator);
1384  let assemblyFormat =
1385    "$operand `,` $accumulator attr-dict `:` type($accumulator)";
1386  let hasVerifier = 1;
1387}
1388
1389#endif // OPENMP_OPS
1390