source: CIVL/include/impls/civl-omp.cvl@ 4e993a9

main test-branch
Last change on this file since 4e993a9 was ea777aa, checked in by Alex Wilton <awilton@…>, 3 years ago

Moved examples, include, build_default.properties, common.xml, and README out from dev.civl.com into the root of the repo.

git-svn-id: svn://vsl.cis.udel.edu/civl/trunk@5704 fb995dde-84ed-4084-dfe6-e5aef3e2452c

  • Property mode set to 100644
File size: 19.4 KB
Line 
1// civlc-omp.cvl: implementations of functions and types in civlc-omp.cvh
2#include<civl-omp.cvh>
3#include<civlc.cvh>
4#include<mem.cvh>
5#include<pointer.cvh>
6#include<seq.cvh>
7#include<concurrency.cvh>
8#include<domain.cvh>
9#pragma CIVL ACSL
10/* Completes the definition of struct OMP_gshared given in civlc-omp.cvh.
11 */
12struct OMP_gshared {
13 int nthreads;
14 _Bool init[]; // which threads have joined
15 $omp_shared shares[];
16 void * original; // pointer to original variable
17};
18
19/* Completes the definition of struct OMP_shared given in civlc-omp.cvh.
20 */
21struct OMP_shared {
22 $omp_gshared gshared;
23 /* The thread id */
24 int tid;
25 /* Pointer to the local copy of the shared variable.
26 * This provides the thread's "private view" of the variable. */
27 void * local;
28 /* Pointer to the local status variable */
29 void * status;
30};
31
32/* Completes the definition of struct OMP_work_record
33 * given in civlc-omp.cvh. */
34struct OMP_work_record {
35 int kind; // loop, barrier, sections, or single
36 int location; // location in model of construct
37 _Bool arrived; // has this thread arrived yet?
38 $domain loop_domain; // full loop domain; null if not loop
39 $domain subdomain; // tasks this thread must do
40 // reduction operation?
41};
42
43struct OMP_gteam {
44 /* scope in which data is allocated in heap */
45 $scope scope;
46 /* number of threads in team */
47 int nthreads;
48 /* which threads have joined this gteam */
49 _Bool init[];
50 /* work queues. Length nthreads. For each thread,
51 * a FIFO queue of work records */
52 /* work queues. Width nthreads. For each worksharing region,
53 * one entry of size nthreads is added. */
54 $omp_work_record work[][];
55 /* the shared object data. */
56 $omp_gshared shared[];
57 $gbarrier gbarrier;
58 /* r/w sets for each thread */
59 $mem read_sets[];
60 $mem write_sets[];
61};
62
63struct OMP_team {
64 $omp_gteam gteam;
65 $scope scope;
66 int tid;
67 $omp_shared shared[];
68 $barrier barrier;
69};
70
71struct OMP_HELPER_SIGNAL {
72 int value;
73};
74
75/* *********************** Functions *********************** */
76
77$atomic_f $omp_gteam $omp_gteam_create($scope scope, int nthreads) {
78 $omp_gteam result = ($omp_gteam)$malloc(scope, sizeof(struct OMP_gteam));
79 _Bool f = $false;
80 $mem empty = $mem_empty();
81
82 //$seq_init(&empty, 0, NULL);
83 result->scope = scope;
84 result->nthreads = nthreads;
85 $seq_init(&result->init, nthreads, &f);
86 $seq_init(&result->work, 0, NULL);
87 $seq_init(&result->shared, 0, NULL);
88 $seq_init(&result->read_sets, nthreads, &empty);
89 $seq_init(&result->write_sets, nthreads, &empty);
90 result->gbarrier = $gbarrier_create(scope, nthreads);
91 return result;
92}
93
94$atomic_f void $omp_gteam_destroy($omp_gteam gteam) {
95 int length = $seq_length(&gteam->work);
96
97 $assert(length == 0, "There are still %d queued worksharing events.", length);
98 $free(gteam->gbarrier);
99 $free(gteam);
100}
101
102/* creates new local team object for a specific thread. */
103$atomic_f $omp_team $omp_team_create($scope scope, $omp_gteam gteam, int tid) {
104 $omp_team result = ($omp_team)$malloc(scope, sizeof(struct OMP_team));
105
106 $assert(!gteam->init[tid], "Thread %d has already joined gteam",
107 tid);
108 gteam->init[tid] = $true;
109 result->gteam = gteam;
110 result->scope = scope;
111 result->tid = tid;
112 $seq_init(&result->shared, 0, NULL);
113 result->barrier = $barrier_create(scope, gteam->gbarrier, tid);
114 return result;
115}
116
117/* destroys the local team object */
118$atomic_f void $omp_team_destroy($omp_team team) {
119 /*int numShared = $seq_length(&team->shared);
120
121 for(int i = 0; i < numShared; i++){
122 $free(team->shared[i]);
123 }*/
124 $free(team->barrier);
125 $free(team);
126}
127
128/* creates new global shared object, associated to the given
129 * global team. A pointer to the shared variable that this
130 * object corresponds to is given.
131 */
132$atomic_f $omp_gshared $omp_gshared_create($omp_gteam gteam,
133 void *original) {
134 $omp_gshared result =
135 ($omp_gshared)$malloc(gteam->scope, sizeof(struct OMP_gshared));
136 _Bool f = $false;
137 $omp_shared null = NULL;
138
139 result->nthreads = gteam->nthreads;
140 $seq_init(&result->init, gteam->nthreads, &f);
141 result->original = original;
142 $seq_init(&result->shares, gteam->nthreads, &null);
143 //result->status = status;
144 return result;
145}
146
147/* destroys the global shared object, copying the content
148 * to the original variable.
149 */
150$atomic_f void $omp_gshared_destroy($omp_gshared gshared) {
151 $free(gshared);
152}
153
154$atomic_f $omp_shared $omp_shared_create($omp_team team,
155 $omp_gshared gshared, void *local, void *status) {
156 $omp_shared result =
157 ($omp_shared)$malloc(team->scope, sizeof(struct OMP_shared));
158 //void *statusRefs[];
159 //int numStatusRefs;
160 int sharedLength;
161
162 $assert(!gshared->init[team->tid], "Thread %d has already created its local copy for %p.\n",
163 team->tid, gshared);
164 result->gshared = gshared;
165 result->tid = team->tid;
166 result->local = local;
167 // copies the shared data to the local copy
168 $copy(local, gshared->original);
169 result->status = status;
170 // set all leaf nodes of status to FULL
171 $set_leaf_nodes(status, FULL);
172 //$leaf_node_ptrs(&statusRefs, status);
173 //numStatusRefs = $seq_length(&statusRefs);
174 //for(int i = 0; i < numStatusRefs; i++)
175 //*((int*)(statusRefs[i])) = FULL;
176 sharedLength = $seq_length(&team->shared);
177 $seq_insert(&team->shared, sharedLength, &result, 1);
178 //$seq_append(&team->shared, &result, 1);
179 gshared->shares[team->tid] = result;
180 return result;
181}
182
183$atomic_f void $omp_shared_destroy($omp_shared shared) {
184 $free(shared);
185}
186
187int $omp_modified_tid($omp_shared shared, void *ref){
188 int tid = shared->tid;
189 $omp_gshared gshared = shared->gshared;
190 int nthreads = gshared->nthreads;
191 _Bool isIdentityRef = $is_identity_ref(ref);
192
193 for(int i=0; i < nthreads;i++){
194 if(i == tid)
195 continue;
196 else{
197 $omp_shared other = gshared->shares[i];
198 $omp_var_status other_status;
199 _Bool segFault = $false;
200
201 if(!$is_derefable_pointer(other))
202 continue;
203 if(isIdentityRef){
204 segFault = $has_leaf_node_equal_to(other->status, MODIFIED);
205 }else{
206 int *other_status_ref = (int*)$translate_ptr(ref, other->status);
207
208 segFault = (*other_status_ref) == MODIFIED;
209 }
210 if(segFault)
211 return i;
212 }
213 }
214 return -1;
215}
216
217void $omp_read($omp_shared shared, void *result, void *ref) {
218 $atomic{
219 int otherTid = $omp_modified_tid(shared, ref);
220
221 if (otherTid >= 0) {
222 void * p = $translate_ptr(ref, shared->gshared->original);
223
224
225 $assert($false, "Thread %d can not safely read memory location %p, "
226 "because thread %d has written to\nthat memory location and hasn't flushed yet.",
227 shared->tid, p, otherTid);
228 }
229 if($is_identity_ref(ref)){
230 if($leaf_nodes_equal_to(shared->status, EMPTY)){
231 $copy(ref, shared->gshared->original); // copy shared to local
232 $set_leaf_nodes(shared->status, FULL);
233 }
234 }else{
235 int *status_ref = (int*)$translate_ptr(ref, shared->status);
236 int status = *status_ref;
237
238 if(status == EMPTY){
239 void *global = $translate_ptr(ref, shared->gshared->original);
240
241 $copy(ref, global); // copy shared to local
242 *status_ref = FULL; // set status to FULL
243 }
244 }
245 // read local
246 $copy(result, ref);
247 }
248}
249
250void $omp_write($omp_shared shared, void *ref, void *value) {
251 $atomic{
252 int otherTid = $omp_modified_tid(shared, ref);
253
254 if (otherTid >= 0) {
255 void *p = $translate_ptr(ref, shared->gshared->original);
256
257 $assert($false, "Thread %d can not safely write to memory location %p, because thread %d has\n"
258 "written to that memory location and hasn't flushed yet.",
259 shared->tid, p, otherTid);
260 }
261 $copy(ref, value);
262 if($is_identity_ref(ref)){
263 $set_leaf_nodes(shared->status, MODIFIED);
264 }else{
265 int *status_ref = (int*)$translate_ptr(ref, shared->status);
266 *status_ref = MODIFIED;
267 }
268 }
269}
270
271/* Only applicable to scalar type? */
272/*@ depends_on \access(shared, local, shared->gshared->original); */
273$atomic_f void $omp_apply_assoc($omp_shared shared,
274 $operation op,
275 void *local){
276 void *shared_ref = $translate_ptr(local, shared->gshared->original);
277
278 $apply(shared_ref, op, local, shared_ref);
279}
280
281
282$atomic_f void $omp_reduction_combine(
283 $operation op, void *reduction_final, void *reduction_local
284) {
285 $read_set_pop();
286 $write_set_pop();
287 $apply(reduction_final, op, reduction_local, reduction_final);
288 $read_set_push();
289 $write_set_push();
290}
291
292void $omp_flush($omp_shared shared, void *ref) {
293 // need to drill down into all leaf nodes of the object
294 // being flushed...
295 // also, it should be ok to flush a memory unit if you are not
296 // the owner but you also have no reads or writes to that variable
297 // TODO: assert there is at most one thread for which this memory unit has status MODIFIED;
298 $omp_gshared gshared = shared->gshared;
299 int tid = shared->tid;
300 void *refs[];
301 int numRefs;
302
303 // get all leaf node pointers
304 $leaf_node_ptrs(&refs, ref);
305 numRefs = $seq_length(&refs);
306 for(int i = 0; i < numRefs; i++){
307 void *leaf = refs[i];
308 int *leaf_status = (int *)$translate_ptr(leaf, shared->status);
309 //void *leaf_local = (int *)$translate_ptr(leaf, shared->local);
310 void *leaf_shared = (int *)$translate_ptr(leaf, gshared->original);
311
312 switch(*leaf_status){
313 case EMPTY:
314 break;
315 case MODIFIED:
316 $copy(leaf_shared, leaf);
317 case FULL:
318 *leaf_status = EMPTY;
319 $set_default(leaf);
320 break;
321 default:
322 $assert(0, "Unknown omp shared variable status: %d", *leaf_status);
323 }
324 }
325}
326
327void $omp_flush_all($omp_team team) {
328 int num_shared = $seq_length(&team->shared);
329
330 for (int i=0; i<num_shared; i++) {
331 $omp_shared shared = team->shared[i];
332
333 $omp_flush(shared, shared->local);
334 }
335}
336
337$atomic_f void $check_data_race($omp_team team) {
338 int cur_tid = team->tid;
339 $mem out_s0 = $mem_empty();
340 $mem out_s1 = $mem_empty();
341 $mem cur_mw = $write_set_pop();
342 $mem cur_mr = $read_set_pop();
343
344 // Update current R/W sets
345 team->gteam->write_sets[cur_tid] = cur_mw;
346 team->gteam->read_sets[cur_tid] = cur_mr;
347 // Check data race.
348 for (int tmp_tid = 0; tmp_tid < team->gteam->nthreads; tmp_tid++) {
349 if (tmp_tid == cur_tid) continue;
350
351 $mem tmp_mr = team->gteam->read_sets[tmp_tid];
352 $mem tmp_mw = team->gteam->write_sets[tmp_tid];
353
354 $assert($mem_no_intersect(cur_mr, tmp_mw, &out_s0, &out_s1),
355 "Data-race detected: %p read by thread %d intersects %p written by thread %d\n",
356 out_s0, cur_tid, out_s1, tmp_tid);
357 $assert($mem_no_intersect(cur_mw, tmp_mr, &out_s0, &out_s1),
358 "Data-race detected: %p read by thread %d intersects %p written by thread %d\n",
359 out_s0, cur_tid, out_s1, tmp_tid);
360 $assert($mem_no_intersect(cur_mw, tmp_mw, &out_s0, &out_s1),
361 "Data-race detected: %p written by thread %d intersects %p written by thread %d\n",
362 out_s0, cur_tid, out_s1, tmp_tid);
363 }
364 // Update current R/W sets
365 team->gteam->write_sets[cur_tid] = $mem_empty();
366 team->gteam->read_sets[cur_tid] = $mem_empty();
367 $read_set_push();
368 $write_set_push();
369}
370
371void $read_and_write_set_update($omp_team team) {
372 team->gteam->write_sets[team->tid] = $write_set_peek();
373 team->gteam->read_sets[team->tid] = $read_set_peek();
374}
375
376void $omp_barrier($omp_team team) {
377 $mem cur_mw = $write_set_pop();
378 $mem cur_mr = $read_set_pop();
379 $mem out_s0 = $mem_empty();
380 $mem out_s1 = $mem_empty();
381 int cur_tid = team->tid;
382
383 team->gteam->write_sets[cur_tid] = cur_mw;
384 team->gteam->read_sets[cur_tid] = cur_mr;
385 for (int tmp_tid = 0; tmp_tid < team->gteam->nthreads; tmp_tid++) {
386 if (tmp_tid == cur_tid) continue;
387
388 $mem tmp_mr = team->gteam->read_sets[tmp_tid];
389 $mem tmp_mw = team->gteam->write_sets[tmp_tid];
390
391 $assert($mem_no_intersect(cur_mr, tmp_mw, &out_s0, &out_s1),
392 "Data-race detected: %p read by thread %d intersects %p written by thread %d\n",
393 out_s0, cur_tid, out_s1, tmp_tid);
394 $assert($mem_no_intersect(cur_mw, tmp_mr, &out_s0, &out_s1),
395 "Data-race detected: %p read by thread %d intersects %p written by thread %d\n",
396 out_s0, cur_tid, out_s1, tmp_tid);
397 $assert($mem_no_intersect(cur_mw, tmp_mw, &out_s0, &out_s1),
398 "Data-race detected: %p written by thread %d intersects %p written by thread %d\n",
399 out_s0, cur_tid, out_s1, tmp_tid);
400 }
401 $barrier_call(team->barrier);
402
403 $read_set_push();
404 $write_set_push();
405}
406
407/**
408 * performs the checking of workrecords when the last thread arrives the workshared region,
409 * including:
410 * number of workrecords in the entry should be nthreads
411 * all workrecords are arrived
412 * all workrecords have the same location as specified
413 * all workrecords are of the same kind
414 */
415void $omp_check_workrecords($omp_gteam gteam, int location, $omp_worksharing_kind kind, int index){
416 int nthreads = gteam->nthreads;
417 int numWorkrecords;
418
419 numWorkrecords = $seq_length(&gteam->work[index]);
420 $assert(numWorkrecords == nthreads, "The %d-th workshared entry has length %d while the expected length should be %d.", index, numWorkrecords, nthreads);
421 for(int i = 0; i < nthreads; i++){
422 $omp_work_record workrecord = gteam->work[index][i];
423
424 $assert(workrecord.arrived, "The workshared event of location %d for thread %d hasn't been arrived yet.",
425 location, i);
426 $assert(workrecord.location == location, "Wrong workshared location of thread %d: expected %d, but saw %d.",
427 i, location, workrecord.location);
428 $assert(workrecord.kind == kind, "Wrong workshared kind of thread %d: expected %d but saw %d",
429 i, kind, workrecord.kind);
430 }
431}
432
433/* finds the workrecord of the given location for a certain thread in a team,
434 * stores the workrecord in the buffer provided,
435 * returns the index of the workrecod entry in the work queue
436 * or -1 if the workrecord is absent.
437 *
438 * gteam: the global team object
439 * tid: the id of the thread
440 * location: the location of the workrecord being searched for
441 * result: the buffer for store the workrecord found
442 */
443int find_work_record($omp_gteam gteam, int tid, int location, $omp_work_record *result){
444 int numEntires = $seq_length(&gteam->work);
445
446 for(int i = 0; i < numEntires; i++){
447 $omp_work_record workrecord = gteam->work[i][tid];
448
449 if(workrecord.location == location){
450 *result = workrecord;
451 return i;
452 }
453 }
454 return -1;
455}
456
457/* checks if all threads have arrived the workrecord at the given index */
458_Bool all_arrived($omp_gteam gteam, int index){
459 for(int tid = 0; tid < gteam->nthreads; tid++){
460 if(!gteam->work[index][tid].arrived)
461 return $false;
462 }
463 return $true;
464}
465
466void arrive($omp_gteam gteam, int tid, int index,
467$omp_work_record workrecord, $omp_worksharing_kind kind, int location){
468 $assert(!workrecord.arrived, "The loop at location %d has been arrived before by thread %d!",
469 location, tid);
470 $assert(workrecord.kind == kind, "Wrong workshare kind of thread %d: expected LOOP",
471 tid);
472 (gteam->work[index][tid]).arrived = $true;
473 if(all_arrived(gteam, index)){//this is the last thread
474 $assert(index == 0, "Inconsistent order or worksharing events.");
475 $omp_check_workrecords(gteam, location, kind, index);
476 $seq_remove(&gteam->work, index, NULL, 1);
477 }
478}
479
480/* TODO: checks if loop_dom gets changed if the current thread is NOT the
481 * first thread entering the loop.
482 */
483$domain $omp_arrive_loop($omp_team team, int location, $domain loop_dom,
484 $domain_strategy strategy){
485 $atomic{
486 $omp_gteam gteam = team->gteam;
487 int tid = team->tid;
488 int nthreads = gteam->nthreads;
489 int index;
490 $omp_work_record workrecord;
491
492 $assert(gteam->init[tid], "The current thread %d has not joined the gteam!", tid);
493 if(nthreads == 1)
494 return loop_dom;
495 index = find_work_record(gteam, tid, location, &workrecord);
496 if(index >= 0){//workrecord found
497 $assert($equals(&gteam->work[index][0].loop_domain, &loop_dom), "The loop domain at location %d is changed when thread %d arrives it. \n Expected %s but saw %s.",
498 location,
499 tid,
500 gteam->work[index][0].loop_domain,
501 loop_dom);
502 arrive(gteam, tid, index, workrecord, LOOP, location);
503 return workrecord.subdomain;
504 }else{//workrecord not found, this should be the first thread
505 $omp_work_record workrecords[nthreads];
506 $domain_decomposition decomposition;
507 int numEntries = $seq_length(&gteam->work);
508
509 decomposition = $domain_partition(loop_dom, strategy, nthreads);
510 workrecords[0].loop_domain = loop_dom;
511 for(int i = 0; i< nthreads; i++){
512 workrecords[i].kind = LOOP;
513 workrecords[i].location = location;
514 workrecords[i].arrived = (i == tid);
515 workrecords[i].subdomain = decomposition.subdomains[i];
516 }
517 $seq_insert(&gteam->work, numEntries, &workrecords, 1);
518 return workrecords[tid].subdomain;
519 }
520 }
521}
522
523/* TODO: need strategies? */
524$domain(1) $omp_arrive_sections($omp_team team, int location, int numSections){
525 $atomic{
526 $omp_gteam gteam = team->gteam;
527 int tid = team->tid;
528 int nthreads = gteam->nthreads;
529 int index;
530 $omp_work_record workrecord;
531
532 $assert(gteam->init[tid], "The current thread %d has not joined the gteam!", tid);
533 if(nthreads == 1)
534 return ($domain(1)) {0 .. numSections - 1 # 1};
535 index = find_work_record(gteam, tid, location, &workrecord);
536 if(index >= 0){//workrecord found
537 arrive(gteam, tid, index, workrecord, SECTIONS, location);
538 return workrecord.subdomain;
539 }else{//workrecord not found, this should be the first thread
540 $omp_work_record workrecords[nthreads];
541 int numEntries = $seq_length(&gteam->work);
542
543 for(int i = 0; i < nthreads; i++){
544 int low = i, high = numSections - 1, step = nthreads;
545 $range range = low .. high # step;
546 $domain(1) dom = ($domain(1)) {range};
547
548 workrecords[i].kind = SECTIONS;
549 workrecords[i].location = location;
550 workrecords[i].arrived = (i == tid);
551 workrecords[i].subdomain = dom;
552 }
553 $seq_insert(&gteam->work, numEntries, &workrecords, 1);
554 return workrecords[tid].subdomain;
555 }
556 }
557}
558
559/* TODO: need strategies? */
560 int $omp_arrive_single($omp_team team, int location){
561 $atomic{
562 $omp_gteam gteam = team->gteam;
563 int tid = team->tid;
564 int nthreads = gteam->nthreads;
565 int index;
566 $omp_work_record workrecord;
567
568 $assert(gteam->init[tid], "The current thread %d has not joined the gteam!", tid);
569 if(nthreads == 1)
570 return tid;
571 index = find_work_record(gteam, tid, location, &workrecord);
572 if(index >= 0){//workrecord found
573 arrive(gteam, tid, index, workrecord, SINGLE, location);
574 }else{//workrecord not found, this should be the first thread
575 $omp_work_record workrecords[nthreads];
576 int numEntries = $seq_length(&gteam->work);
577
578 for(int i = 0; i < nthreads; i++){
579 workrecords[i].kind = SINGLE;
580 workrecords[i].location = location;
581 workrecords[i].arrived = (i == tid);
582 }
583 $seq_insert(&gteam->work, numEntries, &workrecords, 1);
584 }
585 return 0;
586 }
587}
588
589$atomic_f $omp_helper_signal $omp_helper_signal_create(
590 int initValue
591){
592 $omp_helper_signal result;
593 result.value = initValue;
594
595 return result;
596}
597
598$atomic_f void $omp_helper_signal_wait(
599 $omp_helper_signal *signal, int waitValue
600){
601 $read_set_push();
602 $write_set_push();
603 $when(signal->value == waitValue) signal->value = -(signal->value)-1 /* do nothing */;
604 $read_set_pop();
605 $write_set_pop();
606}
607
608$atomic_f void $omp_helper_signal_send(
609 $omp_helper_signal *signal, int sendValue
610){
611 $read_set_push();
612 $write_set_push();
613 signal->value = sendValue;
614 $read_set_pop();
615 $write_set_pop();
616}
617
Note: See TracBrowser for help on using the repository browser.