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