Changes between Version 77 and Version 78 of Next-GenOpenMPTransformation


Ignore:
Timestamp:
12/02/21 15:47:30 (4 years ago)
Author:
wuwenhao
Comment:

Distribute the whole content to different wiki pages.

Legend:

Unmodified
Added
Removed
Modified
  • Next-GenOpenMPTransformation

    v77 v78  
    11= INDEX =
    2 [https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#a1.TheSequentiallyConsistentSubsetofOpenMP 1. The Sequentially Consistent Subset of OpenMP]\\
    3 [https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#a2.SupportedOpenMPFeatures 2. Supported OpenMP Features]\\
    4 [https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#a3.OpenMPSimplification 3. OpenMP Simplification]\\
    5 [https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#a4.OpenMPTransformations 4. OpenMP Transofrmation]
    62
    7 = General Ideas of Omp Transformation =
     3== 1 [wiki:OpenMP-Transformation-Introduction] ==
    84
    9 1. At a right mover (e.g. lock, enter_critical, enter_atomic), we do yield and push.
    10 2. At a left mover (e.g., unlock, exit_critical, exit_atomic), we do check and then clean (pop).
    11     (Note that it only clean whats in between a pair of right mover and left mover.)
    12 3. A barrier is transformed as: barrier() ; $check_data_race(); $track_clean(); barrier();
    13 4. Read and write operations are tracked in $atomic scope and functions/primitives of tracking read/write operations are:
    14   - {{{$track{}}} . . . . . . . . . . . . . . . . creates a new pair of read/write sets for tracking.
    15   - {{{} // end of $track scope}}} . . removes read/write sets currently used for tracking.
    16   - {{{$track_empty();}}} . . . . . . . . . . empties read/write sets currently used for tracking.
    17   - {{{$tracked_reads();}}} . . . . . . . . returns a set of tracked shared variables read by its caller process.
    18   - {{{$tracked_writes();}}} . . . . . . . returns a set of tracked shared variables written by its caller process.
    19 5. {{{$check_data_race}}} asserts that:
    20 {{{
    21   $forall (int i, j: 0 .. nthreads - 1) (i != j) ==>
    22     $mem_disjoint(
    23       writes[i],
    24       $mem_union(reads[j], writes[j])
    25     );
    26 }}}
     5  1.1 [The Sequentially Consistent Subset of OpenMP]
    276
    28 E.g.,
    29 {{{
    30 #pragma omp parallel shared(s)
    31 {
    32   A;
    33   #pragma omp critical(c)
    34   {
    35     B;
    36   }
    37   C;
    38 }
    39 }}}
     7  1.2 [General Ideas of OpenMP Transformation]
    408
    41  ==>
     9== 2 [wiki:OpenMP-Transformation-Supported-Features] ==
    4210
    43 {{{
    44 void $check_data_race($mem *rs, $mem *ws, int n, int tid) {
    45   rs[tid] = $tracked_reads();
    46   ws[tid] = $tracked_writes();
    47   $assert(
    48     $forall (int j: 0 .. nthreads - 1) (j != tid) ==>
    49       $mem_disjoint(ws[tid], $mem_union(rs[j], ws[j])) &&
    50       $mem_disjoint(...)
    51   );
    52 }
    53 }}}
    54 {{{
    55 // Init parallel region env.
    56 $range thread_range = 0 .. nthreads - 1;
    57 $domain(1) dom = ($domain){thread_range};
    58 $omp_gteam gteam = $omp_gteam_create($here, nthreads);
    59 $mem rs[nthreads], ws[nthreads]; // read sets and write sets.
    60 for(int i=0; i<nthreads; i++) {
    61   ws[i] = $mem_empty();
    62   rs[i] = $mem_empty();
    63 }
     11  2.1 [Supported OpenMP Directives]
    6412
    65 // Transformed parallel region
    66 $for (int tid: dom) {
    67   $depends_on(/*nothing*/)$atomic{
    68     $omp_team team = $omp_team_create($here, gteam, tid);
    69     $track{ // RS stack: rs0:={}; WS stack: ws0:={};
    70       // Enter parallel region
    71       A;
    72       $yield();
    73       $when(critical_c->value == 0);
    74       $track{ // Ignore race on lock
    75          critical_c->value = 1; // Enter critical block
    76       }
    77       B;
    78       $track{ // Ignore race on lock
    79          critical_c->value = 0; // Exit critical block
    80       }
    81       // RS stack: rs0:={A, B}; WS stack: ws0:={A, B};
    82       $check_data_race(&rs, &ws, nthreads, tid); // 1st check
    83       $track_clean();
    84       rs[tid] = $mem_empty();
    85       ws[tid] = $mem_empty();
    86       // RS stack: rs0:={}; WS stack: ws0:={};
    87       C;
    88       $check_data_race(&rs, &ws, nthreads, tid); // 2nd check
    89     } // RS stack: <empty>; WS stack: <empty>; empty sets rs0, ws0;
    90   } // end $atomic
    91 } // end $for
    92 }}}
     13    2.1.1 [`parallel` Construct]
    9314
    94 We denotes that thread x executes statement A as Ax.
    95 Assuming there are two threads 0 and 1, two traces shall be explored:
     15    2.1.2 [Worksharing Constructs]
    9616
    97 Trace1: A0y0 A1y1 B0cdr0C0cdr0 B1cdr1C1cdr1;
     17    2.1.3 [Loop-Related Directives]
    9818
    99 Trace2: A0y0 A1y1 B1cdr1C1cdr1 B0cdr0C0cdr0;
     19    2.1.4 [Synchronization Constructs and Clauses]
    10020
    101 For the first trace, cdr0 following B0 checks data races between
     21    2.1.5 [Data Environment]
    10222
    103 A0 and A1;
    104 B0 and A1;
     23  2.2. [Supported OpenMP Runtime Library Types and Rountines]
    10524
    106 Then, w/r sets on thread 0 is cleaned.
     25    2.2.1 [Execution Environment Rountines]
    10726
    108 The second cdr0 following C0 checks data races between
     27    2.2.2 [Lock Routines]
    10928
    110 C0 and A1;
     29    2.2.3 [Timing Routines]
    11130
    112 An implicit barrier is placed at the end after the second cdr0,
    113 thus thread 1 starts executing its remaining part.
    114 The first cdr1 checks data races between
    115 
    116 C0 and A1;
    117 C0 and B1;
    118 
    119 Then, tracked sets on both A1 and B1 are cleaned.
    120 
    121 Finally, the last cdr1 in the first trace checks data races between
    122 
    123 C0 and C1;
    124 
    125 The second trace will complete checking by performing checks between
    126 
    127 B1 and A0
    128 C1 and A0
    129 C1 and B0
    130 
    131 Because B is protected by critical section, races between B0 and B1 are not checked.
    132 As a result, all required pairs are checked.
    133 
    134 
    135 
    136 = 1. The Sequentially Consistent Subset of OpenMP =
    137 OpenMP programs that:
    138 * Do not use non-sequentially consistent atomic directives;
    139 * Do not rely on the accuracy of a false result from omp_test_lock and omp_test_nest_lock; and
    140 * Correctly avoid data races as required in Section 1.4.1 on page 23 (OpenMP spec 5.0) ([https://www.openmp.org/wp-content/uploads/OpenMP-API-Specification-5.0.pdf spec])
    141 
    142 The relaxed consistency model is invisible for such programs, and any explicit flush operations in such programs are redundant.
    143 
    144 = 2. Supported OpenMP Features =
    145 == OpenMP Constructs ==
    146 
    147 * `parallel` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Translatingparallel detail])\\
    148  * `private(`list`)`
    149  * `firstprivate(list)`
    150  * `copyin(list)` . . . . . . . . .[Unsupported]
    151  * `shared(`list`)`
    152  * `default(`option`)` . . . . . .[Unsupported]
    153   * `none`
    154   * `shared`
    155  * `num_threads(`n`)`
    156  * `reduction(op:list)` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Case1:parallelreduction detail])
    157 
    158 * `sections` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Translatingsections detail])
    159  * `private(`list`)`
    160  * `firstprivate(list)`
    161  * `lastprivate(list)` . . . . .[In-progress]
    162  * `reduction(op:list)`
    163  * `nowait`
    164 
    165 * `section` (see `sections`)
    166 
    167 * `single` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Translatingsingle detail])
    168  * `private(`list`)`
    169  * `firstprivate(list)`
    170  * `copyprivate(list)` . . . . .[Unsupported]
    171  * `nowait`
    172  
    173 * `for` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Translatingfor detail])
    174  * `private(`list`)`
    175  * `firstprivate(list)`
    176  * `lastprivate(list)` . . . . .[In-progress]
    177  * `reduction` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Case2:forreduction detail])
    178  * `schedule` . . . . . . . . . . . . [Unsupported]
    179  * `collapse`
    180  * `nowait`
    181 
    182 * `simd` . . . . . . . . . . . . . . . . . . . . [Unsupported]
    183  * `safelen(n)`
    184  * `linear(n)`
    185  * `aligned(n)`
    186  * `private`
    187  * `lastprivate`
    188  * `reduction`
    189  * `collapse`
    190 
    191 * `for simd` . . . . . . . . . . . . . . . . .[Unsupported]
    192  * `safelen(n)`
    193  * `linear(n)`
    194  * `aligned(n)`
    195  * `private`
    196  * `lastprivate`
    197  * `reduction`
    198  * `collapse`
    199  * `firstprivate`
    200  * `nowait`
    201  * `schedule`
    202 
    203 * `declare simd` . . . . . . . . . . . . . [Unsupported]
    204  * `simdlen(n)`
    205  * `linear`
    206  * `aligned(n)`
    207  * `uniform`
    208  * `inbranch`
    209  * `notinbranch`
    210 
    211 * `master` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Translatingmaster detail])
    212 
    213 * `critical` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Translatingcritical detail])
    214  * `[name]`
    215 
    216 * `barrier` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Translatingbarrier detail])
    217 
    218 * `atomic` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Translatingatomic detail])
    219  * atomic-clause
    220   * `read`
    221   * `write`
    222   * `update`
    223   * `capture`
    224  * `seq_cst` . . . . . . . . . . . . . [Unsupported]
    225 
    226 * `ordered` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#Translatingordered detail])
    227 
    228 == OpenMP Types ==
    229 * `omp_lock_t` ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPSimpleLock1 detail])
    230 
    231 == OpenMP Runtime Library Routines ==
    232 === Execution Environment Routines ===
    233 * `omp_get_max_threads()` (translated as `_omp_thread_max`)
    234 * `omp_get_num_threads()` (translated as `_omp_nthreads`)
    235 * `omp_set_num_threads(int x)` (translated as `_omp_num_threads =`/x/)
    236 * `omp_get_thread_num()`(translated as `_omp_tid`)
    237 === Lock Routines ===
    238 ==== OpenMP Simple Lock ====
    239 * `omp_init_lock(omp_lock_t *)`
    240 * `omp_destroy_lock(omp_lock_t *)`
    241 * `omp_set_lock(omp_lock_t *)`
    242 * `omp_unset_lock(omp_lock_t *)`
    243 * `omp_test_lock(omp_lock_t *)`
    244 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPSimpleLock1 detail])
    245 === Timing Routines ===
    246 * `omp_get_wtime()` ([https://vsl.cis.udel.edu/trac/civl/browser/CIVL/trunk/src/include/civl/omp.cvl omp.cvl])
    247 
    248 == Plan ==
    249 * We are going to get rid of the current OMP2CIVL transformer and come up a new transformer that assumes given OpenMP programs are sequentially consistent
    250 * We are gong to improve the current OmpSimplifier using pointer alias analysis
    251 * Note that an atomic construct without `seq_cst` is outside of the  sequentially consistent subset of the language, we need a way to deal with that.
    252 
    253 == Notes ==
    254 * Currently, the simplifier is not aware of the cases that out-of-bound access on multiple dimensional arrays can raise data race.  For example,
    255 {{{
    256 int a[10][5];
    257 #pragma omp parallel for
    258 for (int i = 0; i < 5; i++)
    259   for (int j = 1; j < 10; j++)
    260     a[i][j] = a[i][j-1] // a[0][4] and a[1][-1] refer to the same element
    261 }}}
    262 
    263 The current simplifier will incorrectly sequentialize the example above without realizing the fact that this example is sequentializable if and only if no "logical" out-of-bound happens during the execution.
    264 A fix for the simplifier could be sequentializing the example with inserted assertion for making sure that there is no "logical" out-of-bound error.
    265 
    266 == Related ==
    267 * [[wiki: StaticAnalysis| Static Analysis]]
    268 
    269 
    270 = 3. OpenMP Simplification =
    271 == OpenMP Simplifier ==
    272 * We improve the existing OpenMP simplifier with the informations provided by the [[wiki: StaticAnalysis| Static Analysis]].
    273 * Example 1: (`DRB067-restrictpointer1-orig-no.c` from [https://github.com/LLNL/dataracebench/releases/tag/v1.2.0 DataRaceBench v1.2.0])
    274 {{{
    275 #include <stdlib.h>
    276 typedef double real8;
    277 
    278 void foo(real8 * restrict newSxx, real8 * restrict newSyy, int length)
    279 {
    280   int i;
    281 
    282 #pragma omp parallel for private (i) firstprivate (length)
    283   for (i = 0; i <= length - 1; i += 1) {
    284     newSxx[i] = 0.0;
    285     newSyy[i] = 0.0;
    286   }
    287 }
    288 
    289 int main()
    290 {
    291   int length=10;
    292   real8* newSxx = malloc (length* sizeof (real8));
    293   real8* newSyy = malloc (length* sizeof (real8));
    294 
    295   foo(newSxx, newSyy, length);
    296 
    297   free (newSxx);
    298   free (newSyy);
    299   return 0;
    300 }
    301 }}}
    302   * The OpenMP simplifier analyzes the parallel region with points-to informations about the two pointer argument `newSxx` and `newSyy` from static analysis.
    303   * The OpenMP simplifier can determine that no data race will happen in the parallel region as long as no array out-of-bound error happens in it.
    304   * The OpenMP simplifier sequentializes the program which will be checked by CIVL.  Any possible array out-of-bound error will be caught by CIVL.
    305 
    306 * Example 2 : ( `DRB014-outofbounds-orig-yes.c` from  [https://github.com/LLNL/dataracebench/releases/tag/v1.2.0 DataRaceBench v1.2.0] )
    307 {{{
    308 #include <stdio.h>
    309 int main(int argc, char* argv[])
    310 {
    311   int i,j;
    312   int n=10, m=10;
    313   double b[n][m];
    314 #pragma omp parallel for private(j)
    315   for (i=1;i<n;i++)
    316     for (j=0;j<m;j++) // Note there will be out of bound access                                                               
    317       b[i][j]=b[i][j-1];
    318 
    319   printf ("b[50][50]=%f\n",b[50][50]);
    320 
    321   return 0;
    322 }
    323 }}}
    324   * OpenMP Simplifier proves that `b[i][j]` and `b[i][j-1]` that accessed by different threads which hold different values of `i` will have no data race as long as no array out-of-bound error happens.
    325   * OpenMP Simplifier sequentializes the OpenMP program while the verification condition for array out-of-bound freedom will be checked by CIVL by default.
    326   * The CIVL back-end runs the sequentialized program and detects the array out-of-bound access.
    327 
    328 ==  The Current Approach For Model Checking OpenMP Programs ==
    329 * Example:
    330 {{{
    331 #include <omp.h>
    332 int main()
    333 {
    334   int a[10],i;
    335 
    336 #pragma omp parallel for
    337   for(i=0; i<10; i++)
    338     {
    339       int c;
    340 
    341       c = i % 2;
    342       a[i + c] = 0;
    343     }
    344 }
    345 }}}
    346   * OpenMP Simplifier cannot prove data-race freedom for this program.
    347   * OpenMP2CIVL Transformer will transform this program to a equivalent CIVL-C program for model checking. Here is the code snippet for the CIVL-C program after transformation:
    348   {{{
    349 int main() {
    350   int  a[10];
    351   {
    352     {
    353       int _elab_i = 0;
    354       for (; _elab_i < _omp_num_threads; _elab_i = _elab_i + 1)
    355         ;
    356     }
    357     int $sef$0 = $choose_int(_omp_num_threads);
    358     int _omp_nthreads = 1 + $sef$0;
    359     $range _omp_thread_range = 0 .. _omp_nthreads - 1;
    360     $domain(1) _omp_dom = ($domain){_omp_thread_range};
    361     $omp_gteam _omp_gteam = $omp_gteam_create($here, _omp_nthreads);
    362     $omp_gshared _omp_a_gshared = $omp_gshared_create(_omp_gteam, &(a));
    363     $parfor (int _omp_tid: _omp_dom) {
    364       $omp_team _omp_team = $omp_team_create($here, _omp_gteam, _omp_tid);
    365       int  _omp_a_local[10];
    366       int  _omp_a_status[10];
    367       $omp_shared _omp_a_shared = $omp_shared_create(_omp_team, _omp_a_gshared, &(_omp_a_local), &(_omp_a_status));
    368       {
    369         $range _omp_r1 = 1 .. 10 - 1 # 1;
    370         $domain(1) _omp_loop_domain = ($domain){_omp_r1};
    371         $domain $sef$1 = $omp_arrive_loop(_omp_team, 0, ($domain)_omp_loop_domain, 2);
    372         $domain(1) _omp_my_iters = ($domain(1))$sef$1;
    373         $for (int _omp_i_private: _omp_my_iters) {
    374           int c;
    375           c = _omp_i_private % 2;
    376           int _omp_write0;
    377           _omp_write0 = 0;
    378           $omp_write(_omp_a_shared, &(_omp_a_local[c]), &(_omp_write0));
    379         }
    380       }
    381       $omp_barrier_and_flush(_omp_team);
    382       $omp_shared_destroy(_omp_a_shared);
    383       $omp_team_destroy(_omp_team);
    384     }
    385     $omp_gshared_destroy(_omp_a_gshared);
    386     $omp_gteam_destroy(_omp_gteam);
    387   }
    388 }
    389   }}}
    390   * CIVL reports PROVABLE errors:
    391   {{{
    392 Thread 1 can not safely write to memory location &<d5>a[0], because thread 0 has
    393 written to that memory location and hasn't flushed yet.
    394 
    395 Violation 0 encountered at depth 64:
    396 CIVL execution violation in p2 (kind: ASSERTION_VIOLATION, certainty: PROVEABLE)
    397 at OpenMPTransformer "_omp_a_shared, &(_om" inserted by OpenMPTransformer.a_sharedWriteCall before civlc.cvh:105.14-20 "$malloc"
    398 Assertion: false
    399  -> false
    400 . . .
    401 Call stacks:
    402 process 0:
    403   main at OpenMPTransformer "$parfor (int _omp_ti" inserted by OpenMPTransformer.parallelPragma before civlc.cvh:105.14-20 "$malloc"
    404 process 1:
    405   $barrier_exit at concurrency.cvl:58.2-6 "$when" called from
    406   $barrier_call at concurrency.cvl:63.2-14 "$barrier_exit" called from
    407   $omp_barrier_and_flush at civl-omp.cvl:322.2-14 "$barrier_call" called from
    408   _par_proc0 at OpenMPTransformer "_omp_team" inserted by OpenMPTransformer.barrierAndFlushCall before civlc.cvh:105.14-20 "$malloc"
    409 process 2:
    410   $omp_write at civl-omp.cvl:247.4-10 "$assert" called from
    411   _par_proc0 at OpenMPTransformer "_omp_a_shared, &(_om" inserted by OpenMPTransformer.a_sharedWriteCall before civlc.cvh:105.14-20 "$malloc"
    412 process 3:
    413   $omp_arrive_loop at civl-omp.cvl:405.2-8 "$atomic" called from
    414   _par_proc0 at OpenMPTransformer "_omp_team, 0, ($doma" inserted by OpenMPTransformer.myItersDeclaration before civlc.cvh:105.14-20 "$malloc"
    415 
    416 Logging new entry 0, writing trace to CIVLREP/testOmp5_0.trace
    417 Terminating search after finding 1 violation.
    418 
    419   }}}
    420 
    421 == The New Approach for Model Checking OpenMP programs ==
    422 * Using CIVL's new infrastructure that captures reads / writes at runtime
    423 * If the OpenMP Simplifier fails to sequentialize a program,  transform the OpenMP program to the following form for model checking:
    424 {{{
    425 $mem writes[nthreads], reads[nthreads];
    426 $parfor (int tid:0..nthreads-1) {
    427   $write_set_push();
    428   $read_set_push();
    429   block1;
    430   // barrier
    431   writes[tid] = $write_set_pop();                                                                                                               
    432   reads[tid] = $read_set_pop();
    433   // check for dataraces (collective operation)                                                                           
    434   $write_set_push();
    435   $read_set_push();
    436   // barrier                                                                                                                 
    437   stmt2;
    438   // barrier
    439   writes[tid] = $write_set_pop();                                                                                                               
    440   reads[tid] = $read_set_pop();                                                                                                                 
    441   // check for dataraces (collective operation)
    442 }
    443 }}}
    444 * Functions for managing `$mem` objects can be found in `mem.cvh`
    445 
    446 
    447 = 4. OpenMP Transformations =
    448 == Support Types ==
    449 
    450 * `$omp_gteam`: global team object, represents a team of threads executing in a parallel region.  A handle type.  This is where all the state needed to correctly execute a parallel region will be stored.   This includes a global barrier and a worksharing queue (incomplete array-of-$omp_work_record) for every thread. Definition:
    451 {{{
    452 typedef struct OMP_gteam {
    453   $scope scope;
    454   int nthreads;
    455   _Bool init[];
    456   $omp_work_record work[][];
    457   $gbarrier gbarrier;
    458 } * $omp_gteam;
    459 }}}
    460 
    461 * `$omp_team`: local object belonging to a single thread and referencing the global team object.  A handle type. It also includes a local barrier. Definition:
    462 {{{
    463 typedef struct OMP_team {
    464   $omp_gteam gteam;
    465   $scope scope;
    466   int tid;
    467   $barrier barrier;
    468 } * $omp_team;
    469 }}}
    470 
    471 * `$omp_work_record`: the worksharing information that a thread needs for executing a worksharing region. It contains the kind of the worksharing region, the location of the region, the status of the region and the subdomain (iterations/sections/task assigned to the thread).
    472 {{{
    473 typedef struct OMP_work_record {
    474   int kind; // loop, barrier, sections, or single
    475   int location; // location in model of construct
    476   _Bool arrived; // has this thread arrived yet?
    477   $domain loop_dom;// full loop domain; null if not loop
    478   $domain subdomain; // tasks this thread must do
    479 } $omp_work_record;
    480 }}}
    481 
    482 == Support Functions ==
    483 
    484 === Team creation and destruction ===
    485 
    486 * `$omp_gteam $omp_gteam_create($scope scope, int nthreads)`
    487  * creates new global team object, allocating object in heap in the specified scope.  Number of threads that will be in the team is `nthreads`.
    488 
    489 * `void $omp_gteam_destroy($omp_gteam gteam)`
    490  * destroys the global team object.  All shared objects associated to the team must have been destroyed before calling this function.
    491 
    492 * `$omp_team $omp_team_create($scope scope, $omp_gteam gteam, int tid)`
    493  * creates new local team object for a specific thread.
    494 
    495 * `void $omp_team_destroy($omp_team team)`
    496  * destroys the local team object
    497 
    498 === Other ===
    499 
    500 * `void $omp_apply_assoc(void * x, $operation op, void * y)`
    501  * translation of `x = x op y`
    502 
    503 * `void $omp_flush_all($omp_team)`
    504  * performs an OpenMP flush operation on all shared objects.  This is the default in OpenMP if no argument is specified for a flush construct.
    505 
    506 === Worksharing and barriers ===
    507 
    508 * `void $omp_barrier($omp_team team)`
    509  * performs a barrier only.  Note however that usually (always?) a barrier is accompanied by a flush-all, so `$omp_barrier_and_flush` should be used instead.
    510 
    511 * `void $omp_barrier_and_flush($omp_team team)`
    512  * checks data race violations by examining read & write set intersections
    513  * combines a barrier and a flush on all shared objects owned by the team.  Implicit in many OpenMP worksharing constructs.
    514 
    515 
    516 * `$domain $omp_arrive_loop($omp_team team, int location, $domain loop_dom,  $DecompositionStrategy strategy)`
    517  * called by a thread when it reaches an omp for loop, this function returns the subset of the loop domain specifying the iterations that this thread will execute.  The dimension of the domain returned equals the dimension of the given domain `loop_dom`.
    518 
    519 * `$domain(1) $omp_arrive_sections($omp_team team, int location, int numSections)`
    520  * called by a thread when it reaches an omp sections construct, this function returns the subset of the integers 0..numSections-1 specifying the indexes of the sections that this thread will execute.  The sections are numbered from 0 in increasing order.
    521 
    522 * `int $omp_arrive_single($omp_team team, int location)`
    523  * called by a thread when it reaches on omp single construct, returns the thread ID of the thread that will execute the single construct.
    524 
    525 
    526 == Worksharing model ==
    527 
    528 This section describes how the system functions dealing with worksharing are implemented.
    529 
    530 The global data structure `$omp_gteam` contains a FIFO queue for each thread.   The queue contains work-sharing records, one record for each work-sharing or barrier construct encountered.    The record contains the basic information about the construct as provided by the arguments to the arrival function, as well as the distribution chosen for that thread.
    531 
    532 The constructs are a lot like MPI collective operations, and are modeled similarly.
    533 
    534 When a thread arrives at one of these constructs, it invokes the relevant arrival function.  At this point you can determine whether this thread is the first to arrive at that construct.  If its queue is empty, it is the first, otherwise it is not first, and the oldest entry in its queue will be the entry corresponding to this construct.
    535 
    536 When a thread is the first thread to arrive at a construct, a distribution is chosen for every thread and a record is created and enqueued in each thread queue (including the caller).   The distributions can be chosen nondeterministically, possibly with some restrictions to achieve some tractability/soundness compromise.  The record for this thread is then dequeued and the iterator returned.
    537 
    538 If a thread is not the first to arrive, its record is dequeued and compared with the arguments given in the function call.  They should match, and if they don't, an error is reported.  This indicates that either threads encountered constructs in different orders or the loop parameters changed.
    539 
    540 
    541 == Translations of specific directives ==
    542 
    543 === Translating `parallel` ===
    544 
    545 `parallel`: this spawns some nondeterministic number of threads, unless a `num_threads` clause is present, in which case the number of threads is specified exactly..  We will assume there is a constant `THREAD_MAX` defined somewhere.  The number of threads created will be between 1 and `THREAD_MAX` (inclusive).  Each thread is assigned an ID.  The original ("master") thread has ID 0.  All threads execute the parallel region.
    546 
    547 {{{
    548   float a; // shared
    549   int i; // private
    550   int j; // firstprivate
    551   #pragma omp parallel shared(a) private(i) firstprivate(j)
    552   {
    553     BLOCK
    554   }
    555 }}}
    556 
    557 =>
    558 
    559 {{{
    560   float a; // shared
    561   int i; // private
    562   int j; // firstprivate
    563   { // parallel construct (begin)
    564     int nthreads = 1+$choose_int(num_threads);
    565     int fstpvt_j = j;
    566     $omp_gteam gteam = $omp_gteam_create($here, nthreads);
    567 
    568     $parfor (int tid : ($domain){0..nthreads-1}) {
    569       $local_start();
    570       $omp_team team = $omp_team_create($here, gteam, tid);
    571       $read_set_push(); $write_set_push();
    572 
    573       int i; // private
    574       int j = fstpvt_j; // first private init.
    575 
    576       transfromed(BLOCK)
    577 
    578       $omp_barrier(team); //check data race included
    579 
    580       $read_set_pop(); $write_set_pop();
    581       $omp_team_destroy(team);
    582       $local_end();
    583     }
    584 
    585     $omp_gteam_destroy(gteam);
    586   } // parallel construct (end)
    587 }}}
    588 
    589 All variables that occur in the parallel construct, i.e., the lexical extent of the parallel construct, must be determined to be either private or shared.   This is determined by the clauses and the default rules as specified in the OpenMP Standard.  Obviously any variable declared within the construct itself must be private.
    590 
    591 For all '''private''' variables `i` and `j` (out of `$parfor`) not declared within
    592 the parallel construct, create new variables of the same type, `i` and `j` (in the `$parfer`).
    593 The new variable is declared within the thread scope. 
    594 `j` (out of `$parfor`) is also '''firstprivate''',  then `j` (in the `$parfor`) is
    595 initialized with the value of `fstpvt_j`. 
    596 Otherwise, `i` (in the `$parfor`) is uninitialized, so has an undefined value.
    597 
    598 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    599 
    600 === Translating `sections` ===
    601 
    602 Say there are `numSections` sections.  This number is known statically.
    603 
    604 {{{
    605 #pragma omp sections
    606 #pragma omp section
    607   S1 // section 1
    608 #pragma omp section
    609   S2 // section 2
    610 ...
    611 }}}
    612 
    613 =>
    614 
    615 {{{
    616 {
    617   $domain(1) sections_dist = $omp_arrive_sections(
    618                team, section_id++, numSections);
    619 
    620   $for (int _omp_sid : sections_dist) {
    621     if(_omp_sid == 1) { // section 1
    622       translate(S1);
    623     }
    624     if(_omp_sid == 2) { // section 2
    625       translate(S2);
    626     }
    627     ...
    628   }
    629   $omp_barrier(team);
    630 }
    631 }}}
    632 
    633 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    634 
    635 === Translating `single` ===
    636 
    637 {{{
    638 #pragma omp single
    639   BLOCK
    640 }}}
    641 
    642 =>
    643 
    644 {{{
    645 { // single construct (begin)
    646   int _omp_single_dist = $omp_arrive_single(
    647         team, single_id++);
    648 
    649   if (_omp_tid == _omp_single_dist) {
    650     translate(BLOCK);
    651   }
    652 
    653   $omp_barrier(team);
    654 } // single construct (end)
    655 }}}
    656 
    657 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    658 
    659 === Translating `for` ===
    660 
    661 Try to determine whether the loop iterations are independent.  In that case, they can all be executed by one thread.
    662 Otherwise:
    663 
    664 {{{
    665 #pragma omp for
    666 for (i=1; i<n; i=i+1) {
    667   BLOCK
    668 }
    669 }}}
    670 
    671 =>
    672 
    673 {{{
    674 { // worksharing-loop construct (begin)
    675   // [lb .. ub # step] (lb <= ub)
    676   $domain loop_domain = {1 .. n-1 # 1};
    677   $domain(1) loop_dist = ($domain(1))$omp_arrive_loop(
    678                team, loop_id++, loop_domain, STRATEGY);
    679 
    680   $for (int i : loop_dist) {
    681     transfromed(BLOCK)
    682   }
    683   $omp_barrier(team);
    684 } // worksharing-loop construct (end)
    685 }}}
    686 
    687 We can vary the way the sub-domains are chosen to explore different tradeoffs and strategies.  On one extreme, every kind of partition can be explored; on the other, some fixed strategy like round-robin with chunksize 1 can be used.  This only changes the definition of `$omp_arrive_loop`, not the translation above.
    688 
    689 {{{
    690 #pragma omp parallel for collapse(3)
    691 for (i=0; i<n; i++)
    692   for (j=0; j<m; j++)
    693     for (k=0; k<l; k++) {
    694       BLOCK
    695     }
    696 }}}
    697 
    698 =>
    699 
    700 {{{
    701 { // worksharing-loop construct (begin)
    702   $domain loop_domain = {0..n-1 #1, 0..m-1 #1, 0..l-1 #1};
    703   $domain(3) loop_dist = ($domain(3))$omp_arrive_loop(
    704                team, loop_id++, loop_domain, STRATEGY);
    705 
    706   $for (int i, j, k : loop_dist) {
    707     transfromed(BLOCK)
    708   }
    709   $omp_barrier(team);
    710 } // worksharing-loop construct (end)
    711 }}}
    712 
    713 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    714 
    715 === Translating `master` ===
    716 
    717 {{{
    718 #pragma omp master
    719   BLOCK
    720 }}}
    721 
    722 =>
    723 
    724 {{{
    725 if (tid == 0) {
    726   translate(BLOCK);
    727 }
    728 }}}
    729 
    730 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    731 
    732 === Translating `barrier` ===
    733 
    734 {{{
    735 #pragma omp barrier
    736 }}}
    737 
    738 =>
    739 
    740 {{{
    741 $omp_barrier(team);
    742 }}}
    743 
    744 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    745 
    746 === Translating `critical` ===
    747 
    748 Basically, use a lock for each critical name, plus one for the "no name".  All threads must obtain lock to enter the critical section, then release it.
    749 i.e., if there are critical sections name a, b, and c, there should be global root-scope variables of boolean type named `_critical_noname`, `_critical_a`, etc. \\
    750 The translation pattern is:
    751 * '''update R/W sets''' for the caller thread that calls `$read_and_write_set_update(team)`
    752 * '''wait''' for other threads until they finish updating their R/W sets, by calling `$yield()`
    753 * '''acquire the lock''', which bonds to this `critical` region.
    754 * '''check data-race''' by calling `$check_data_race(team)` at each synchronizing point.
    755 * '''explore''' the  translated `critical` region.
    756 * '''update R/W sets'''
    757 * '''wait''' for other threads
    758 * '''release the lock'''
    759 * '''check data-race'''
    760 
    761 {{{
    762 // critical block with unspecified name
    763 #pragma omp critical
    764   BLOCK_A
    765 
    766 // critical block with specified name
    767 #pragma omp critical(x)
    768   BLOCK_X
    769 }}}
    770 
    771 =>
    772 
    773 {{{
    774 // global
    775 $omp_helper_semaphore critical_ = $omp_helper_semaphore_create(0);
    776 $omp_helper_semaphore critical_x = $omp_helper_semaphore_create(0);
    777 ...
    778 
    779 // critical block with unspecified name
    780   $read_and_write_set_update(team);
    781   $yield();
    782   $omp_helper_semaphore_p(critical_, 0);
    783   $omp_helper_semaphore_v(critical_, 1);
    784   $check_data_race(team);
    785 
    786   translate(BLOCK_A)
    787 
    788   $read_and_write_set_update(team);
    789   $yield();
    790   $omp_helper_semaphore_v(critical_, 0);
    791   $check_data_race(team);
    792 
    793 // critical block with specified name
    794 
    795   $read_and_write_set_update(team);
    796   $yield();
    797   $omp_helper_semaphore_p(critical_x, 0);
    798   $omp_helper_semaphore_v(critical_x, 1);
    799   $check_data_race(team);
    800 
    801   translate(BLOCK_X);
    802 
    803   $read_and_write_set_update(team);
    804   $yield();
    805   $omp_helper_semaphore_v(critical_x, 0);
    806   $check_data_race(team);
    807 }}}
    808 
    809 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    810 
    811 === Translating `atomic` ===
    812 
    813 In general, the memory location protected by the atomic construct will be isolated by interacting with local intermediate variables, so that data race on other memory locations not protected by this atomic construct can be checked by CIVL.
    814 The local intermediate variable is named based on the protected variable identifier, the kind of atomic construct and a counter ID (for separating distinct atomic constructs, which have same `atomic-clause` for a same variable).
    815 Currently, CIVL only support sequentially consistent atomic operations and will NOT handle non-sequentially consistent ones.
    816 
    817 ==== `read` ====
    818 
    819 {{{
    820 #pragma omp atomic read
    821   v = x; // int type
    822 }}}
    823 
    824 =>
    825 
    826 {{{
    827 int $x_read$0;
    828 $yield(); // Yield on entering an atomic construct
    829 $track{ // Will not collect this read on x for checking data race
    830   $x_read$0 = x;
    831 }
    832 v = $x_read$0;
    833 // Check data race and clean R/W sets of this thread.
    834 $check_data_race(&rs, &ws, nthreads, tid);
    835 $track_clean();
    836 rs[tid] = $mem_empty();
    837 ws[tid] = $mem_empty();
    838 }}}
    839 
    840 ==== `write` ====
    841 
    842 {{{
    843 #pragma omp atomic write
    844   x = EXPR; // int type
    845 }}}
    846 
    847 =>
    848 
    849 {{{
    850 int $x_write$0 = EXPR;
    851 $track{ // Will not collect this write on x for checking data race
    852   x = $x_write$0;
    853 }
    854 }}}
    855 
    856 ==== `update` ====
    857 
    858 {{{
    859 #pragma omp atomic update
    860   x = x BIN_OP EXPR; // int type
    861 }}}
    862 
    863 =>
    864 
    865 {{{
    866 int $x_update$0 = EXPR;
    867 $track { // Will not collect this update on x for checking data race
    868   x = x BIN_OP $x_update$0;
    869 }
    870 }}}
    871 
    872 (All update expression statement can be concluded in this form.)
    873 
    874 ==== `capture` ====
    875 
    876 {{{
    877 #pragma omp atomic capture
    878 {
    879   v = x; // int type
    880   x = x BIN_OP EXPR; // int type
    881 }
    882 }}}
    883 
    884 =>
    885 
    886 {{{
    887 int $x_capture$0;
    888 int $x_capture_delta$0 = EXPR;
    889 $track { // R/W operations on x will not be collected.
    890   $x_capture$0 = x;
    891   x = x BIN_OP $x_capture_delta$0;
    892 }
    893 v = $x_capture$0;
    894 }}}
    895 
    896 (The order of the assignment statement of `$x_capture` and the update
    897 expression statement is corresponding to the order in source code.)
    898 
    899 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    900 
    901 ===  Translating`ordered` ===
    902 
    903 This can only be used inside and OMP `for` loop in which the pragma used the `ordered` clause.  (Check that.)  It indicates that the specified region must be executed in iteration order.
    904 
    905 In this case the system function must return an int iterator in which the ints occur in loop order.
    906 
    907 ==== Case 1: ordered for-loop without a collapse clause ====
    908 {{{
    909 #pragma omp for ordered
    910 for (i=INIT; i<COND; i+=INCR) {
    911   ...
    912   #pragma omp ordered
    913   BLOCK
    914   ...
    915 }
    916 }}}
    917 
    918 =>
    919 
    920 {{{
    921 // global helper lock
    922 $omp_helper_semaphore ordered_0 = $omp_helper_semaphore_create(INIT);
    923   ..
    924 {
    925   $domain loop_domain = {INIT .. COND # INCR};
    926   $domain(1) loop_dist = ($domain(1))$omp_arrive_loop(team, loop_id++, loop_domain, STRATEGY);
    927 
    928   $for (int i : loop_dist) {
    929     int i_next = i + I_INCR;
    930     ...
    931     $read_and_write_set_update(team);
    932     $yield();
    933     // wait until hs0->val is i
    934     $omp_helper_semaphore_p(ordered_0, i);
    935     $check_data_race(team);
    936 
    937     translate(BLOCK);
    938 
    939     $read_and_write_set_update(team);
    940     $yield();
    941     // set hs0->val as i_next
    942     $omp_helper_semaphore_v(ordered_0, i_next);
    943     $check_data_race(team);
    944     ...
    945   }
    946 }
    947 }}}
    948 
    949 ==== Case 2: ordered for-loop with a collapse clause ====
    950 The transformation pattern is:
    951 * '''record next iteration value''' for all loop variables in the entry of the loop body.
    952 * '''update R/W sets''' for the current thread.
    953 * '''wait for other threads''' finishing R/W set updates.
    954 * '''semaphore P operations''' from the outer-most to the inner-most loop variable.
    955   (This operation waits for a "signal" send when its sequentially previous iteration is completed.)
    956 * '''check data-race''' at any synchronizing point.
    957 * '''explore''' the translated `ordered` region
    958 * '''update R/W sets'''
    959 * '''wait for others'''
    960 * '''semaphore V operations''' from the inner-most to the outer-most loop variable;
    961   (This operation issues a "signal" to enable the execution of its sequentially next iteration.)
    962 * '''check data-race'''
    963 
    964 {{{
    965 #pragma omp parallel for collapse(3) ordered
    966 for (i=I_INIT; i<I_COND; i+=I_INCR)
    967   for (j=J_INIT; j<J_COND; j+=J_INCR)
    968     for (k=K_INIT; k<K_COND; k+=K_INCR) {
    969       #pragma omp ordered
    970         BLOCK
    971   }
    972 }}}
    973 
    974 =>
    975 
    976 {{{
    977 // global helper semaphore
    978 $omp_helper_semaphore ordered_0 = $omp_helper_semaphore_create(I_INIT);
    979 $omp_helper_semaphore ordered_1 = $omp_helper_semaphore_create(J_INIT);
    980 $omp_helper_semaphore ordered_2 = $omp_helper_semaphore_create(K_INIT);
    981 
    982 { // worksharing-loop construct (begin)
    983   ..
    984   $for (int i, j, k: loop_dist) {
    985     int i_next = i + I_INCR;
    986     int j_next = j + J_INCR;
    987     int k_next = k + K_INCR;
    988 
    989     $read_and_write_set_update(team);
    990     $yield();
    991     $omp_helper_semaphore_p(ordered_0, i);
    992     $omp_helper_semaphore_p(ordered_1, j);
    993     $omp_helper_semaphore_p(ordered_2, k);
    994     $check_data_race(team);
    995 
    996     translate(BLOCK);
    997 
    998     $read_and_write_set_update(team);
    999     $yield();
    1000     if (k_next < K_COND) // 'k' deos NOT hit its bound
    1001       $omp_helper_semaphore_v(ordered_2, k_next); // incr 'k'
    1002     else {
    1003       $omp_helper_semaphore_v(ordered_2, K_INIT); // reset 'k'
    1004 
    1005       if (j_next < J_COND) // 'j' deos NOT hit its bound
    1006         $omp_helper_semaphore_v(ordered_1, j_next); // incr 'j'
    1007       else {
    1008         $omp_helper_semaphore_v(ordered_1, J_INIT); // reset 'j'
    1009 
    1010         $omp_helper_semaphore_v(ordered_0, i_next); // incr 'i'
    1011       }
    1012     }
    1013     $check_data_race(team);
    1014   }
    1015   ..
    1016 } // worksharing-loop construct (end)
    1017 }}}
    1018 
    1019 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    1020 
    1021 === Translating `nowait` ===
    1022 
    1023 Just leave out the `$omp_barrier_and_flush` at the end of the translated construct.
    1024 
    1025 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    1026 
    1027 === Translating `reduction` ===
    1028 ==== Case 1: parallel reduction ====
    1029 {{{
    1030 int sum = sum_base;
    1031 int prod = prod_base;
    1032 
    1033 #pragma parallel shared(a) reduction(+:sum) reduction(*:prod)
    1034 {
    1035   int tid = omp_get_thread_num();
    1036 
    1037   sum += a[tid];
    1038   prod *= a[tid];
    1039 }
    1040 }}}
    1041 
    1042 =>
    1043 
    1044 {{{
    1045 int sum = sum_base;
    1046 int prod = prod_base;
    1047 
    1048 { // parallel construct (begin)
    1049   ..
    1050   $parfor (..) {
    1051     ..
    1052     int *_omp_rdc_0_sum = &sum;
    1053     int sum = 0;
    1054     int *_omp_rdc_1_prod = &prod;
    1055     int prod = 1;
    105631   
    1057     int tid = _omp_tid;
    1058    
    1059     sum += a[tid];
    1060     prod *= a[tid];   
    1061 
    1062     $omp_reduction_combine(ADD, _omp_rdc_0_sum, sum);
    1063     $omp_reduction_combine(MULT, _omp_rdc_1_prod, prod);
    1064 
    1065     $omp_barrier(team); //check data race
    1066     ..
    1067   }
    1068   ..
    1069 } // parallel construct (end)
    1070 }}}
    1071 
    1072 ==== Case 2: for reduction ====
    1073 
    1074 {{{
    1075 #pragma omp for reduction(+: fsum, isum)
    1076 for (i=a; i<b; i+=c) {
    1077   BLOCK
    1078   fsum += EXPR0;
    1079   isum += EXPR1;
    1080 }
    1081 }}}
    1082 
    1083 =>
    1084 
    1085 {{{
    1086 { // worksharing-loop construct (begin)
    1087   $domain loop_domain = {a..b-1#c};
    1088   $domain(1) loop_dist = ($domain(1))$omp_arrive_loop(
    1089                team, loop_id++, loop_domain, STRATEGY);
    1090  
    1091   float *_omp_rdc_0_fsum = &fsum;
    1092   float fsum = 0.0;
    1093   int *_omp_rdc_1_isum = &isum;
    1094   int isum = 0;
    1095 
    1096   $for (int i : loop_dist) {
    1097     translate(BLOCK)
    1098     fsum += translate(EXPR0);
    1099     isum += translate(EXPR1);
    1100   }
    1101 
    1102   $omp_reduction_combine(ADD, _omp_rdc_0_fsum, fsum);
    1103   $omp_reduction_combine(ADD, _omp_rdc_1_isum, isum);
    1104 
    1105   $omp_barrier(team);
    1106 } // worksharing-loop construct (end)
    1107 }}}
    1108 
    1109 In `Case1` the combination is performed in `$parfor` after the completion of
    1110 the last statement in the original structured block.
    1111 In `Case2` the combination is performed immediately after the end of `$for`.
    1112 
    1113 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])
    1114 
    1115 == Translations of specific routines ==
    1116 
    1117 === Lock Routines ===
    1118 ==== OpenMP Simple Lock ====
    1119 
    1120 * `omp_lock_t`, `omp_init_lock` and `omp_destroy_lock` stay unchanged ([https://vsl.cis.udel.edu/trac/civl/browser/CIVL/trunk/src/include/civl/omp.cvl omp.cvl])
    1121 * `omp_set_lock` and `omp_unset_lock` are translated as:
    1122 
    1123 {{{
    1124   ..
    1125   omp_set_lock(&lck);
    1126   BLOCK;
    1127   omp_unset_lock(&lck);
    1128   ..
    1129 }}}
    1130 
    1131 =>
    1132 
    1133 {{{
    1134   ..
    1135   $read_and_write_set_update(team);
    1136   $yield();
    1137   $omp_set_lock(&lck, tid);
    1138   $check_data_race(team);
    1139 
    1140   translated(BLOCK);
    1141 
    1142   $read_and_write_set_update(team);
    1143   $yield();
    1144   $omp_unset_lock(&lck, tid);
    1145   $check_data_race(team);
    1146   ..
    1147 }}}
    1148 
    1149 * `omp_test_lock` is translated as:
    1150 
    1151 {{{
    1152   while(! omp_test_lock(&lck)) {
    1153     .. // Other non-critical task
    1154   }
    1155   .. // Critical task
    1156   omp_unset_lock(&lck);
    1157 }}}
    1158 
    1159 =>
    1160 
    1161 {{{
    1162   while(! omp_test_lock(&lck, tid)) { // no Sync, not wrapped
    1163     .. // Other non-critical task
    1164   }
    1165   .. // Critical task
    1166   $read_and_write_set_update(team);
    1167   $yield();
    1168   $omp_unset_lock(&lck, tid); // Sync required, so wrapped.
    1169   $check_data_race(team);
    1170 }}}
    1171 
    1172 ([https://vsl.cis.udel.edu/trac/civl/wiki/Next-GenOpenMPTransformation#OpenMPConstructs back to top])