xref: /llvm-project/flang/docs/OpenMP-semantics.md (revision b7ff03206d668cd5a620a9d4e1b22ea112ed56e3)
1<!--===- docs/OpenMP-semantics.md
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# OpenMP Semantic Analysis
10
11```{contents}
12---
13local:
14---
15```
16
17## OpenMP for F18
18
191. Define and document the parse tree representation for
20    * Directives (listed below)
21    * Clauses (listed below)
22    * Documentation
231. All the directives and clauses need source provenance for messages
241. Define and document how an OpenMP directive in the parse tree
25will be represented as the parent of the statement(s)
26to which the directive applies.
27The parser itself will not be able to construct this representation;
28there will be subsequent passes that do so
29just like for example _do-stmt_ and _do-construct_.
301. Define and document the symbol table extensions
311. Define and document the module file extensions
32
33
34### Directives
35
36OpenMP divides directives into three categories as follows.
37The directives that are in the same categories share some characteristics.
38
39
40
41#### Declarative directives
42
43An OpenMP directive may only be placed in a declarative context.
44A declarative directive results in one or more declarations only;
45it is not associated with the immediate execution of any user code.
46
47List of existing ones:
48* declare simd
49* declare target
50* threadprivate
51* declare reduction
52* requires
53
54There is a parser node for each of these directives and
55the parser node saves information associated with the directive,
56for example,
57the name of the procedure-name in the `declare simd` directive.
58
59Each parse tree node keeps source provenance,
60one for the directive name itself and
61one for the entire directive starting from the directive name.
62
63A top-level class, `OpenMPDeclarativeConstruct`,
64holds all four of the node types as discriminated unions
65along with the source provenance for the entire directive
66starting from `!$OMP`.
67
68In `parser-tree.h`,
69`OpenMPDeclarativeConstruct` is part
70of the `SpecificationConstruct` and `SpecificationPart`
71in F18 because
72a declarative directive can only be placed in the specification part
73of a Fortran program.
74
75All the `Names` or `Designators` associated
76with the declarative directive will be resolved in later phases.
77
78#### Executable directives
79
80An OpenMP directive that is **not** declarative.
81That is, it may only be placed in an executable context.
82It contains stand-alone directives and constructs
83that are associated with code blocks.
84The stand-alone directive is described in the next section.
85
86The constructs associated with code blocks listed below
87share a similar structure:
88_Begin Directive_, _Clause List_, _Code Block_, _End Directive_.
89The _End Directive_ is optional for constructs
90like Loop-associated constructs.
91
92* Block-associated constructs (`OpenMPBlockConstruct`)
93* Loop-associated constructs (`OpenMPLoopConstruct`)
94* Atomic construct (`OpenMPAtomicConstruct`)
95* Sections Construct (`OpenMPSectionsConstruct`,
96  contains Sections/Parallel Sections constructs)
97* Critical Construct (`OpenMPCriticalConstruct`)
98
99A top-level class, `OpenMPConstruct`,
100includes stand-alone directive and constructs
101listed above as discriminated unions.
102
103In the `parse-tree.h`, `OpenMPConstruct` is an element
104of the `ExecutableConstruct`.
105
106All the `Names` or `Designators` associated
107with the executable directive will be resolved in Semantic Analysis.
108
109When the backtracking parser can not identify the associated code blocks,
110the parse tree will be rewritten later in the Semantics Analysis.
111
112#### Stand-alone Directives
113
114An OpenMP executable directive that has no associated user code
115except for that which appears in clauses in the directive.
116
117List of existing ones:
118* taskyield
119* barrier
120* taskwait
121* target enter data
122* target exit data
123* target update
124* ordered
125* flush
126* cancel
127* cancellation point
128
129A higher-level class is created for each category
130which contains directives listed above that share a similar structure:
131* OpenMPSimpleStandaloneConstruct
132(taskyield, barrier, taskwait,
133target enter/exit data, target update, ordered)
134* OpenMPFlushConstruct
135* OpenMPCancelConstruct
136* OpenMPCancellationPointConstruct
137
138A top-level class, `OpenMPStandaloneConstruct`,
139holds all four of the node types as discriminated unions
140along with the source provenance for the entire directive.
141Also, each parser node for the stand-alone directive saves
142the source provenance for the directive name itself.
143
144### Clauses
145
146Each clause represented as a distinct class in `parse-tree.h`.
147A top-level class, `OmpClause`,
148includes all the clauses as discriminated unions.
149The parser node for `OmpClause` saves the source provenance
150for the entire clause.
151
152All the `Names` or `Designators` associated
153with the clauses will be resolved in Semantic Analysis.
154
155Note that the backtracking parser will not validate
156that the list of clauses associated
157with a directive is valid other than to make sure they are well-formed.
158In particular,
159the parser does not check that
160the association between directive and clauses is correct
161nor check that the values in the directives or clauses are correct.
162These checks are deferred to later phases of semantics to simplify the parser.
163
164## Symbol Table Extensions for OpenMP
165
166Name resolution can be impacted by the OpenMP code.
167In addition to the regular steps to do the name resolution,
168new scopes and symbols may need to be created
169when encountering certain OpenMP constructs.
170This section describes the extensions
171for OpenMP during Symbol Table construction.
172
173OpenMP uses the fork-join model of parallel execution and
174all OpenMP threads have access to
175a _shared_ memory place to store and retrieve variables
176but each thread can also have access to
177its _threadprivate_ memory that must not be accessed by other threads.
178
179For the directives and clauses that can control the data environments,
180compiler needs to determine two kinds of _access_
181to variables used in the directive’s associated structured block:
182**shared** and **private**.
183Each variable referenced in the structured block
184has an original variable immediately outside of the OpenMP constructs.
185Reference to a shared variable in the structured block
186becomes a reference to the original variable.
187However, each private variable referenced in the structured block,
188a new version of the original variable (of the same type and size)
189will be created in the threadprivate memory.
190
191There are exceptions that directives/clauses
192need to create a new `Symbol` without creating a new `Scope`,
193but in general,
194when encountering each of the data environment controlling directives
195(discussed in the following sections),
196a new `Scope` will be created.
197For each private variable referenced in the structured block,
198a new `Symbol` is created out of the original variable
199and the new `Symbol` is associated
200with original variable’s `Symbol` via `HostAssocDetails`.
201A new set of OpenMP specific flags are added
202into `Flag` class in `symbol.h` to indicate the types of
203associations,
204data-sharing attributes,
205and data-mapping attributes
206in the OpenMP data environments.
207
208### New Symbol without new Scope
209
210OpenMP directives that require new `Symbol` to be created
211but not new `Scope` are listed in the following table
212in terms of the Symbol Table extensions for OpenMP:
213
214<table>
215  <tr>
216   <td rowspan="2" colspan="2" >Directives/Clauses
217   </td>
218   <td rowspan="2" >Create New
219<p>
220Symbol
221<p>
222w/
223   </td>
224   <td colspan="2" >Add Flag
225   </td>
226  </tr>
227  <tr>
228   <td>on Symbol of
229   </td>
230   <td>Flag
231   </td>
232  </tr>
233  <tr>
234   <td rowspan="4" >Declarative Directives
235   </td>
236   <td>declare simd [(proc-name)]
237   </td>
238   <td>-
239   </td>
240   <td>The name of the enclosing function, subroutine, or interface body
241   to which it applies, or proc-name
242   </td>
243   <td>OmpDeclareSimd
244   </td>
245  </tr>
246  <tr>
247   <td>declare target
248   </td>
249   <td>-
250   </td>
251   <td>The name of the enclosing function, subroutine, or interface body
252   to which it applies
253   </td>
254   <td>OmpDeclareTarget
255   </td>
256  </tr>
257  <tr>
258   <td>threadprivate(list)
259   </td>
260   <td>-
261   </td>
262   <td>named variables and named common blocks
263   </td>
264   <td>OmpThreadPrivate
265   </td>
266  </tr>
267  <tr>
268   <td>declare reduction
269   </td>
270   <td>*
271   </td>
272   <td>reduction-identifier
273   </td>
274   <td>OmpDeclareReduction
275   </td>
276  </tr>
277  <tr>
278   <td>Stand-alone directives
279   </td>
280   <td>flush
281   </td>
282   <td>-
283   </td>
284   <td>variable, array section or common block name
285   </td>
286   <td>OmpFlushed
287   </td>
288  </tr>
289  <tr>
290   <td colspan="2" >critical [(name)]
291   </td>
292   <td>-
293   </td>
294   <td>name (user-defined identifier)
295   </td>
296   <td>OmpCriticalLock
297   </td>
298  </tr>
299  <tr>
300   <td colspan="2" >if ([ directive-name-modifier :] scalar-logical-expr)
301   </td>
302   <td>-
303   </td>
304   <td>directive-name-modifier
305   </td>
306   <td>OmpIfSpecified
307   </td>
308  </tr>
309</table>
310
311
312      -      No Action
313
314      *      Discussed in “Module File Extensions for OpenMP” section
315
316
317### New Symbol with new Scope
318
319For the following OpenMP regions:
320
321* `target` regions
322* `target data` regions
323* `teams` regions
324* `parallel` regions
325* `simd` regions
326* task generating regions (created by `task` or `taskloop` constructs)
327* worksharing regions
328(created by `do`, `sections`, `single`, or `workshare` constructs)
329
330A new `Scope` will be created
331when encountering the above OpenMP constructs
332to ensure the correct data environment during the Code Generation.
333To determine whether a variable referenced in these regions
334needs the creation of a new `Symbol`,
335all the data-sharing attribute rules
336described in OpenMP Spec [2.15.1] apply during the Name Resolution.
337The available data-sharing attributes are:
338**_shared_**,
339**_private_**,
340**_linear_**,
341**_firstprivate_**,
342and **_lastprivate_**.
343The attribute is represented as `Flag` in the `Symbol` object.
344
345More details are listed in the following table:
346
347<table>
348  <tr>
349   <td rowspan="2" >Attribute
350   </td>
351   <td rowspan="2" >Create New Symbol
352   </td>
353   <td colspan="2" >Add Flag
354   </td>
355  </tr>
356  <tr>
357   <td>on Symbol of
358   </td>
359   <td>Flag
360   </td>
361  </tr>
362  <tr>
363   <td>shared
364   </td>
365   <td>No
366   </td>
367   <td>Original variable
368   </td>
369   <td>OmpShared
370   </td>
371  </tr>
372  <tr>
373   <td>private
374   </td>
375   <td>Yes
376   </td>
377   <td>New Symbol
378   </td>
379   <td>OmpPrivate
380   </td>
381  </tr>
382  <tr>
383   <td>linear
384   </td>
385   <td>Yes
386   </td>
387   <td>New Symbol
388   </td>
389   <td>OmpLinear
390   </td>
391  </tr>
392  <tr>
393   <td>firstprivate
394   </td>
395   <td>Yes
396   </td>
397   <td>New Symbol
398   </td>
399   <td>OmpFirstPrivate
400   </td>
401  </tr>
402  <tr>
403   <td>lastprivate
404   </td>
405   <td>Yes
406   </td>
407   <td>New Symbol
408   </td>
409   <td>OmpLastPrivate
410   </td>
411  </tr>
412  <tr>
413   <td>use_device_ptr
414   </td>
415   <td>Yes
416   </td>
417   <td>New Symbol
418   </td>
419   <td>OmpUseDevicePtr
420   </td>
421  </tr>
422  <tr>
423   <td>use_device_addr
424   </td>
425   <td>Yes
426   </td>
427   <td>New Symbol
428   </td>
429   <td>OmpUseDeviceAddr
430   </td>
431  </tr>
432</table>
433
434To determine the right data-sharing attribute,
435OpenMP defines that the data-sharing attributes
436of variables that are referenced in a construct can be
437_predetermined_, _explicitly determined_, or _implicitly determined_.
438
439#### Predetermined data-sharing attributes
440
441* Assumed-size arrays are **shared**
442* The loop iteration variable(s)
443in the associated _do-loop(s)_ of a
444_do_,
445_parallel do_,
446_taskloop_,
447or _distributeconstruct_
448is (are) **private**
449* A loop iteration variable
450for a sequential loop in a _parallel_ or task generating construct
451is **private** in the innermost such construct that encloses the loop
452* Implied-do indices and _forall_ indices are **private**
453* The loop iteration variable in the associated _do-loop_
454of a _simd_ construct with just one associated _do-loop_
455is **linear** with a linear-step
456that is the increment of the associated _do-loop_
457* The loop iteration variables in the associated _do-loop(s)_ of a _simd_
458construct with multiple associated _do-loop(s)_ are **lastprivate**
459
460#### Explicitly determined data-sharing attributes
461
462Variables with _explicitly determined_ data-sharing attributes are:
463
464* Variables are referenced in a given construct
465* Variables are listed in a data-sharing attribute clause on the construct.
466
467The data-sharing attribute clauses are:
468* _default_ clause
469(discussed in “Implicitly determined data-sharing attributes”)
470* _shared_ clause
471* _private_ clause
472* _linear_ clause
473* _firstprivate_ clause
474* _lastprivate_ clause
475* _reduction_ clause
476(new `Symbol` created with the flag `OmpReduction` set)
477
478Note that variables with _predetermined_ data-sharing attributes
479may not be listed (with exceptions) in data-sharing attribute clauses.
480
481#### Implicitly determined data-sharing attributes
482
483Variables with implicitly determined data-sharing attributes are:
484
485* Variables are referenced in a given construct
486* Variables do not have _predetermined_ data-sharing attributes
487* Variables are not listed in a data-sharing attribute clause
488on the construct.
489
490Rules for variables with _implicitly determined_ data-sharing attributes:
491
492* In a _parallel_ construct, if no _default_ clause is present,
493these variables are **shared**
494* In a task generating construct,
495if no _default_ clause is present,
496a variable for which the data-sharing attribute
497is not determined by the rules above
498and that in the enclosing context is determined
499to be shared by all implicit tasks
500bound to the current team is **shared**
501* In a _target_ construct,
502variables that are not mapped after applying data-mapping attribute rules
503(discussed later) are **firstprivate**
504* In an orphaned task generating construct,
505if no _default_ clause is present, dummy arguments are **firstprivate**
506* In a task generating construct, if no _default_ clause is present,
507a variable for which the data-sharing attribute is not determined
508by the rules above is **firstprivate**
509* For constructs other than task generating constructs or _target_ constructs,
510if no _default_ clause is present,
511these variables reference the variables with the same names
512that exist in the enclosing context
513* In a _parallel_, _teams_, or task generating construct,
514the data-sharing attributes of these variables are determined
515by the _default_ clause, if present:
516    * _default(shared)_
517    clause causes all variables referenced in the construct
518    that have _implicitly determined_ data-sharing attributes
519    to be **shared**
520    * _default(private)_
521    clause causes all variables referenced in the construct
522    that have _implicitly determined_ data-sharing attributes
523    to be **private**
524    * _default(firstprivate)_
525    clause causes all variables referenced in the construct
526    that have _implicitly determined_ data-sharing attributes
527    to be **firstprivate**
528    * _default(none)_
529    clause requires that each variable
530    that is referenced in the construct,
531    and that does not have a _predetermined_ data-sharing attribute,
532    must have its data-sharing attribute _explicitly determined_
533    by being listed in a data-sharing attribute clause
534
535
536### Data-mapping Attribute
537
538When encountering the _target data_ and _target_ directives,
539the data-mapping attributes of any variable referenced in a target region
540will be determined and represented as `Flag` in the `Symbol` object
541of the variable.
542No `Symbol` or `Scope` will be created.
543
544However, there are some exceptions for this, Pointers that appear in a
545use_device_ptr clause are privatized and the device pointers to the
546corresponding list items in the device data environment are assigned into the
547private versions so it is best to follow the representation for privatised
548variables i.e represent them with a new Symbol and `OmpUseDevicePtr` flag.
549If a list item that appears in a use_device_addr clause has corresponding
550storage in the device data environment, references to the list item in the
551associated structured block are converted into references to the corresponding
552list item so following the same i.e. represent them with a new Symbol and
553`OmpUseDeviceAddr` flag.
554
555The basic steps to determine the data-mapping attribute are:
556
5571. If _map_ clause is present,
558the data-mapping attribute is determined by the _map-type_
559on the clause and its corresponding `Flag` are listed below:
560
561<table>
562  <tr>
563   <td>
564data-mapping attribute
565   </td>
566   <td>Flag
567   </td>
568  </tr>
569  <tr>
570   <td>to
571   </td>
572   <td>OmpMapTo
573   </td>
574  </tr>
575  <tr>
576   <td>from
577   </td>
578   <td>OmpMapFrom
579   </td>
580  </tr>
581  <tr>
582   <td>tofrom
583(default if map-type is not present)
584   </td>
585   <td>OmpMapTo & OmpMapFrom
586   </td>
587  </tr>
588  <tr>
589   <td>alloc
590   </td>
591   <td>OmpMapAlloc
592   </td>
593  </tr>
594  <tr>
595   <td>release
596   </td>
597   <td>OmpMapRelease
598   </td>
599  </tr>
600  <tr>
601   <td>delete
602   </td>
603   <td>OmpMapDelete
604   </td>
605  </tr>
606</table>
607
6082. Otherwise, the following data-mapping rules apply
609for variables referenced in a _target_ construct
610that are _not_ declared in the construct and
611do not appear in data-sharing attribute or map clauses:
612    * If a variable appears in a _to_ or _link_ clause
613    on a _declare target_ directive then it is treated
614    as if it had appeared in a _map_ clause with a _map-type_ of **tofrom**
6153. Otherwise, the following implicit data-mapping attribute rules apply:
616    * If a _defaultmap(tofrom:scalar)_ clause is _not_ present
617    then a scalar variable is not mapped,
618    but instead has an implicit data-sharing attribute of **firstprivate**
619    * If a _defaultmap(tofrom:scalar)_ clause is present
620    then a scalar variable is treated as if it had appeared
621    in a map clause with a map-type of **tofrom**
622    * If a variable is not a scalar
623    then it is treated as if it had appeared in a map clause
624    with a _map-type_ of **tofrom**
625
626After the completion of the Name Resolution phase,
627all the data-sharing or data-mapping attributes marked for the `Symbols`
628may be used later in the Semantics Analysis and in the Code Generation.
629
630## Module File Extensions for OpenMP
631
632After the successful compilation of modules and submodules
633that may contain the following Declarative Directives,
634the entire directive starting from `!$OMP` needs to be written out
635into `.mod` files in their corresponding Specification Part:
636
637* _declare simd_ or _declare target_
638
639    In the “New Symbol without new Scope” section,
640    we described that when encountering these two declarative directives,
641    new `Flag` will be applied to the Symbol of the name of
642    the enclosing function, subroutine, or interface body to
643    which it applies, or proc-name.
644    This `Flag` should be part of the API information
645    for the given subroutine or function
646
647* _declare reduction_
648
649    The _reduction-identifier_ in this directive
650    can be use-associated or host-associated.
651    However, it will not act like other Symbols
652    because user may have a reduction name
653    that is the same as a Fortran entity name in the same scope.
654    Therefore a specific data structure needs to be created
655    to save the _reduction-identifier_ information
656    in the Scope and this directive needs to be written into `.mod` files
657
658## Phases of OpenMP Analysis
659
6601. Create the parse tree for OpenMP
661    1. Add types for directives and clauses
662        1. Add type(s) that will be used for directives
663        2. Add type(s) that will be used for clauses
664        3. Add other types, e.g. wrappers or other containers
665        4. Use std::variant to encapsulate meaningful types
666    2. Implemented in the parser for OpenMP (openmp-grammar.h)
6672. Create canonical nesting
668    1. Restructure parse tree to reflect the association
669    of directives and stmts
670        1. Associate `OpenMPLoopConstruct`
671        with `DoConstruct` and `OpenMPEndLoopDirective`
672    1. Investigate, and perhaps reuse,
673    the algorithm used to restructure do-loops
674    2. Add a pass near the code that restructures do-loops;
675    but do not extend the code that handles do-loop for OpenMP;
676    keep this code separate.
677    3. Report errors that prevent restructuring
678    (e.g. loop directive not followed by loop)
679    We should abort in case of errors
680    because there is no point to perform further checks
681    if it is not a legal OpenMP construct
6823. Validate the structured-block
683    1. Structured-block is a block of executable statements
684    1. Single entry and single exit
685    1. Access to the structured block must not be the result of a branch
686    1. The point of exit cannot be a branch out of the structured block
6874. Check that directive and clause combinations are legal
688    1. Begin and End directive should match
689    1. Simply check that the clauses are allowed by the directives
690    1. Write as a separate pass for simplicity and correctness of the parse tree
6915. Write parse tree tests
692    1. At this point, the parse tree should be perfectly formed
693    1. Write tests that check for correct form and provenance information
694    1. Write tests for errors that can occur during the restructuring
6956. Scope, symbol tables, and name resolution
696    1. Update the existing code to handle names and scopes introduced by OpenMP
697    1. Write tests to make sure names are properly implemented
6987. Check semantics that is specific to each directive
699    1. Validate the directive and its clauses
700    1. Some clause checks require the result of name resolution,
701    i.e. “A list item may appear in a _linear_ or _firstprivate_ clause
702    but not both.”
703    1. TBD:
704    Validate the nested statement for legality in the scope of the directive
705    1. Check the nesting of regions [OpenMP 4.5 spec 2.17]
7068. Module file utilities
707    1.  Write necessary OpenMP declarative directives to `.mod` files
708    2. Update the existing code
709    to read available OpenMP directives from the `.mod` files
710