| | 141 | == 1.3 OpenMP Simplification == |
| | 142 | |
| | 143 | === OpenMP Simplifier === |
| | 144 | * We improve the existing OpenMP simplifier with the informations provided by the [[wiki: StaticAnalysis| Static Analysis]]. |
| | 145 | * Example 1: (`DRB067-restrictpointer1-orig-no.c` from [https://github.com/LLNL/dataracebench/releases/tag/v1.2.0 DataRaceBench v1.2.0]) |
| | 146 | {{{ |
| | 147 | #include <stdlib.h> |
| | 148 | typedef double real8; |
| | 149 | |
| | 150 | void foo(real8 * restrict newSxx, real8 * restrict newSyy, int length) |
| | 151 | { |
| | 152 | int i; |
| | 153 | |
| | 154 | #pragma omp parallel for private (i) firstprivate (length) |
| | 155 | for (i = 0; i <= length - 1; i += 1) { |
| | 156 | newSxx[i] = 0.0; |
| | 157 | newSyy[i] = 0.0; |
| | 158 | } |
| | 159 | } |
| | 160 | |
| | 161 | int main() |
| | 162 | { |
| | 163 | int length=10; |
| | 164 | real8* newSxx = malloc (length* sizeof (real8)); |
| | 165 | real8* newSyy = malloc (length* sizeof (real8)); |
| | 166 | |
| | 167 | foo(newSxx, newSyy, length); |
| | 168 | |
| | 169 | free (newSxx); |
| | 170 | free (newSyy); |
| | 171 | return 0; |
| | 172 | } |
| | 173 | }}} |
| | 174 | * The OpenMP simplifier analyzes the parallel region with points-to informations about the two pointer argument `newSxx` and `newSyy` from static analysis. |
| | 175 | * 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. |
| | 176 | * The OpenMP simplifier sequentializes the program which will be checked by CIVL. Any possible array out-of-bound error will be caught by CIVL. |
| | 177 | |
| | 178 | * Example 2 : ( `DRB014-outofbounds-orig-yes.c` from [https://github.com/LLNL/dataracebench/releases/tag/v1.2.0 DataRaceBench v1.2.0] ) |
| | 179 | {{{ |
| | 180 | #include <stdio.h> |
| | 181 | int main(int argc, char* argv[]) |
| | 182 | { |
| | 183 | int i,j; |
| | 184 | int n=10, m=10; |
| | 185 | double b[n][m]; |
| | 186 | #pragma omp parallel for private(j) |
| | 187 | for (i=1;i<n;i++) |
| | 188 | for (j=0;j<m;j++) // Note there will be out of bound access |
| | 189 | b[i][j]=b[i][j-1]; |
| | 190 | |
| | 191 | printf ("b[50][50]=%f\n",b[50][50]); |
| | 192 | |
| | 193 | return 0; |
| | 194 | } |
| | 195 | }}} |
| | 196 | * 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. |
| | 197 | * OpenMP Simplifier sequentializes the OpenMP program while the verification condition for array out-of-bound freedom will be checked by CIVL by default. |
| | 198 | * The CIVL back-end runs the sequentialized program and detects the array out-of-bound access. |
| | 199 | |
| | 200 | |
| | 201 | === Notes === |
| | 202 | * The OpenMP simplification transformation shall use pointer alias analysis |
| | 203 | * The simplifier currently cannot detect data race caused by out-of-bound access on multiple dimensional arrays. The simplification transformer incorrectly sequentializes the example below without realizing that it can be sequentialized if and only if **no 'logical' out-of-bound happens during the execution**. A possible fix for this issue could be sequentializing the example with inserted assertions for ensuring the absence of 'logical' out-of-bound error. |
| | 204 | |
| | 205 | e.g., |
| | 206 | {{{ |
| | 207 | int a[10][5]; |
| | 208 | #pragma omp parallel for |
| | 209 | for (int i = 0; i < 5; i++) |
| | 210 | for (int j = 1; j < 10; j++) |
| | 211 | a[i][j] = a[i][j-1]; |
| | 212 | //a[0][4] and a[1][-1] refer to the same element |
| | 213 | }}} |
| | 214 | |
| | 215 | == 1.4 Model Checking OpenMP Programs == |
| | 216 | === The Current Approach For Model Checking OpenMP Programs === |
| | 217 | * Example: |
| | 218 | {{{ |
| | 219 | #include <omp.h> |
| | 220 | int main() |
| | 221 | { |
| | 222 | int a[10],i; |
| | 223 | |
| | 224 | #pragma omp parallel for |
| | 225 | for(i=0; i<10; i++) |
| | 226 | { |
| | 227 | int c; |
| | 228 | |
| | 229 | c = i % 2; |
| | 230 | a[i + c] = 0; |
| | 231 | } |
| | 232 | } |
| | 233 | }}} |
| | 234 | * OpenMP Simplifier cannot prove data-race freedom for this program. |
| | 235 | * 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: |
| | 236 | {{{ |
| | 237 | int main() { |
| | 238 | int a[10]; |
| | 239 | { |
| | 240 | { |
| | 241 | int _elab_i = 0; |
| | 242 | for (; _elab_i < _omp_num_threads; _elab_i = _elab_i + 1) |
| | 243 | ; |
| | 244 | } |
| | 245 | int $sef$0 = $choose_int(_omp_num_threads); |
| | 246 | int _omp_nthreads = 1 + $sef$0; |
| | 247 | $range _omp_thread_range = 0 .. _omp_nthreads - 1; |
| | 248 | $domain(1) _omp_dom = ($domain){_omp_thread_range}; |
| | 249 | $omp_gteam _omp_gteam = $omp_gteam_create($here, _omp_nthreads); |
| | 250 | $omp_gshared _omp_a_gshared = $omp_gshared_create(_omp_gteam, &(a)); |
| | 251 | $parfor (int _omp_tid: _omp_dom) { |
| | 252 | $omp_team _omp_team = $omp_team_create($here, _omp_gteam, _omp_tid); |
| | 253 | int _omp_a_local[10]; |
| | 254 | int _omp_a_status[10]; |
| | 255 | $omp_shared _omp_a_shared = $omp_shared_create(_omp_team, _omp_a_gshared, &(_omp_a_local), &(_omp_a_status)); |
| | 256 | { |
| | 257 | $range _omp_r1 = 1 .. 10 - 1 # 1; |
| | 258 | $domain(1) _omp_loop_domain = ($domain){_omp_r1}; |
| | 259 | $domain $sef$1 = $omp_arrive_loop(_omp_team, 0, ($domain)_omp_loop_domain, 2); |
| | 260 | $domain(1) _omp_my_iters = ($domain(1))$sef$1; |
| | 261 | $for (int _omp_i_private: _omp_my_iters) { |
| | 262 | int c; |
| | 263 | c = _omp_i_private % 2; |
| | 264 | int _omp_write0; |
| | 265 | _omp_write0 = 0; |
| | 266 | $omp_write(_omp_a_shared, &(_omp_a_local[c]), &(_omp_write0)); |
| | 267 | } |
| | 268 | } |
| | 269 | $omp_barrier_and_flush(_omp_team); |
| | 270 | $omp_shared_destroy(_omp_a_shared); |
| | 271 | $omp_team_destroy(_omp_team); |
| | 272 | } |
| | 273 | $omp_gshared_destroy(_omp_a_gshared); |
| | 274 | $omp_gteam_destroy(_omp_gteam); |
| | 275 | } |
| | 276 | } |
| | 277 | }}} |
| | 278 | * CIVL reports PROVABLE errors: |
| | 279 | {{{ |
| | 280 | Thread 1 can not safely write to memory location &<d5>a[0], because thread 0 has |
| | 281 | written to that memory location and hasn't flushed yet. |
| | 282 | |
| | 283 | Violation 0 encountered at depth 64: |
| | 284 | CIVL execution violation in p2 (kind: ASSERTION_VIOLATION, certainty: PROVEABLE) |
| | 285 | at OpenMPTransformer "_omp_a_shared, &(_om" inserted by OpenMPTransformer.a_sharedWriteCall before civlc.cvh:105.14-20 "$malloc" |
| | 286 | Assertion: false |
| | 287 | -> false |
| | 288 | . . . |
| | 289 | Call stacks: |
| | 290 | process 0: |
| | 291 | main at OpenMPTransformer "$parfor (int _omp_ti" inserted by OpenMPTransformer.parallelPragma before civlc.cvh:105.14-20 "$malloc" |
| | 292 | process 1: |
| | 293 | $barrier_exit at concurrency.cvl:58.2-6 "$when" called from |
| | 294 | $barrier_call at concurrency.cvl:63.2-14 "$barrier_exit" called from |
| | 295 | $omp_barrier_and_flush at civl-omp.cvl:322.2-14 "$barrier_call" called from |
| | 296 | _par_proc0 at OpenMPTransformer "_omp_team" inserted by OpenMPTransformer.barrierAndFlushCall before civlc.cvh:105.14-20 "$malloc" |
| | 297 | process 2: |
| | 298 | $omp_write at civl-omp.cvl:247.4-10 "$assert" called from |
| | 299 | _par_proc0 at OpenMPTransformer "_omp_a_shared, &(_om" inserted by OpenMPTransformer.a_sharedWriteCall before civlc.cvh:105.14-20 "$malloc" |
| | 300 | process 3: |
| | 301 | $omp_arrive_loop at civl-omp.cvl:405.2-8 "$atomic" called from |
| | 302 | _par_proc0 at OpenMPTransformer "_omp_team, 0, ($doma" inserted by OpenMPTransformer.myItersDeclaration before civlc.cvh:105.14-20 "$malloc" |
| | 303 | |
| | 304 | Logging new entry 0, writing trace to CIVLREP/testOmp5_0.trace |
| | 305 | Terminating search after finding 1 violation. |
| | 306 | |
| | 307 | }}} |
| | 308 | |
| | 309 | === The New Approach for Model Checking OpenMP programs === |
| | 310 | * Using CIVL's new infrastructure that captures reads / writes at runtime |
| | 311 | * If the OpenMP Simplifier fails to sequentialize a program, transform the OpenMP program to the following form for model checking: |
| | 312 | {{{ |
| | 313 | $mem writes[nthreads], reads[nthreads]; |
| | 314 | $parfor (int tid:0..nthreads-1) { |
| | 315 | $write_set_push(); |
| | 316 | $read_set_push(); |
| | 317 | block1; |
| | 318 | // barrier |
| | 319 | writes[tid] = $write_set_pop(); |
| | 320 | reads[tid] = $read_set_pop(); |
| | 321 | // check for dataraces (collective operation) |
| | 322 | $write_set_push(); |
| | 323 | $read_set_push(); |
| | 324 | // barrier |
| | 325 | stmt2; |
| | 326 | // barrier |
| | 327 | writes[tid] = $write_set_pop(); |
| | 328 | reads[tid] = $read_set_pop(); |
| | 329 | // check for dataraces (collective operation) |
| | 330 | } |
| | 331 | }}} |
| | 332 | * Functions for managing `$mem` objects can be found in `mem.cvh` |
| | 333 | |
| | 334 | |