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