LLVM OpenMP* Runtime Library
kmp_taskdeps.cpp
1/*
2 * kmp_taskdeps.cpp
3 */
4
5//===----------------------------------------------------------------------===//
6//
7// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
8// See https://llvm.org/LICENSE.txt for license information.
9// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
10//
11//===----------------------------------------------------------------------===//
12
13//#define KMP_SUPPORT_GRAPH_OUTPUT 1
14
15#include "kmp.h"
16#include "kmp_io.h"
17#include "kmp_wait_release.h"
18#include "kmp_taskdeps.h"
19#if OMPT_SUPPORT
20#include "ompt-specific.h"
21#endif
22
23// TODO: Improve memory allocation? keep a list of pre-allocated structures?
24// allocate in blocks? re-use list finished list entries?
25// TODO: don't use atomic ref counters for stack-allocated nodes.
26// TODO: find an alternate to atomic refs for heap-allocated nodes?
27// TODO: Finish graph output support
28// TODO: kmp_lock_t seems a tad to big (and heavy weight) for this. Check other
29// runtime locks
30// TODO: Any ITT support needed?
31
32#ifdef KMP_SUPPORT_GRAPH_OUTPUT
33static std::atomic<kmp_int32> kmp_node_id_seed = 0;
34#endif
35
36static void __kmp_init_node(kmp_depnode_t *node, bool on_stack) {
37 node->dn.successors = NULL;
38 node->dn.task = NULL; // will point to the right task
39 // once dependences have been processed
40 for (int i = 0; i < MAX_MTX_DEPS; ++i)
41 node->dn.mtx_locks[i] = NULL;
42 node->dn.mtx_num_locks = 0;
43 __kmp_init_lock(&node->dn.lock);
44 // Init creates the first reference. Bit 0 indicates that this node
45 // resides on the stack. The refcount is incremented and decremented in
46 // steps of two, maintaining use of even numbers for heap nodes and odd
47 // numbers for stack nodes.
48 KMP_ATOMIC_ST_RLX(&node->dn.nrefs, on_stack ? 3 : 2);
49#ifdef KMP_SUPPORT_GRAPH_OUTPUT
50 node->dn.id = KMP_ATOMIC_INC(&kmp_node_id_seed);
51#endif
52#if USE_ITT_BUILD && USE_ITT_NOTIFY
53 __itt_sync_create(node, "OMP task dep node", NULL, 0);
54#endif
55}
56
57static inline kmp_depnode_t *__kmp_node_ref(kmp_depnode_t *node) {
58 KMP_ATOMIC_ADD(&node->dn.nrefs, 2);
59 return node;
60}
61
62enum { KMP_DEPHASH_OTHER_SIZE = 97, KMP_DEPHASH_MASTER_SIZE = 997 };
63
64size_t sizes[] = {997, 2003, 4001, 8191, 16001, 32003, 64007, 131071, 270029};
65const size_t MAX_GEN = 8;
66
67static inline size_t __kmp_dephash_hash(kmp_intptr_t addr, size_t hsize) {
68 // TODO alternate to try: set = (((Addr64)(addrUsefulBits * 9.618)) %
69 // m_num_sets );
70 return ((addr >> 6) ^ (addr >> 2)) % hsize;
71}
72
73static kmp_dephash_t *__kmp_dephash_extend(kmp_info_t *thread,
74 kmp_dephash_t *current_dephash) {
75 kmp_dephash_t *h;
76
77 size_t gen = current_dephash->generation + 1;
78 if (gen >= MAX_GEN)
79 return current_dephash;
80 size_t new_size = sizes[gen];
81
82 size_t size_to_allocate =
83 new_size * sizeof(kmp_dephash_entry_t *) + sizeof(kmp_dephash_t);
84
85#if USE_FAST_MEMORY
86 h = (kmp_dephash_t *)__kmp_fast_allocate(thread, size_to_allocate);
87#else
88 h = (kmp_dephash_t *)__kmp_thread_malloc(thread, size_to_allocate);
89#endif
90
91 h->size = new_size;
92 h->nelements = current_dephash->nelements;
93 h->buckets = (kmp_dephash_entry **)(h + 1);
94 h->generation = gen;
95 h->nconflicts = 0;
96 h->last_all = current_dephash->last_all;
97
98 // make sure buckets are properly initialized
99 for (size_t i = 0; i < new_size; i++) {
100 h->buckets[i] = NULL;
101 }
102
103 // insert existing elements in the new table
104 for (size_t i = 0; i < current_dephash->size; i++) {
105 kmp_dephash_entry_t *next, *entry;
106 for (entry = current_dephash->buckets[i]; entry; entry = next) {
107 next = entry->next_in_bucket;
108 // Compute the new hash using the new size, and insert the entry in
109 // the new bucket.
110 size_t new_bucket = __kmp_dephash_hash(entry->addr, h->size);
111 entry->next_in_bucket = h->buckets[new_bucket];
112 if (entry->next_in_bucket) {
113 h->nconflicts++;
114 }
115 h->buckets[new_bucket] = entry;
116 }
117 }
118
119 // Free old hash table
120#if USE_FAST_MEMORY
121 __kmp_fast_free(thread, current_dephash);
122#else
123 __kmp_thread_free(thread, current_dephash);
124#endif
125
126 return h;
127}
128
129static kmp_dephash_t *__kmp_dephash_create(kmp_info_t *thread,
130 kmp_taskdata_t *current_task) {
131 kmp_dephash_t *h;
132
133 size_t h_size;
134
135 if (current_task->td_flags.tasktype == TASK_IMPLICIT)
136 h_size = KMP_DEPHASH_MASTER_SIZE;
137 else
138 h_size = KMP_DEPHASH_OTHER_SIZE;
139
140 size_t size = h_size * sizeof(kmp_dephash_entry_t *) + sizeof(kmp_dephash_t);
141
142#if USE_FAST_MEMORY
143 h = (kmp_dephash_t *)__kmp_fast_allocate(thread, size);
144#else
145 h = (kmp_dephash_t *)__kmp_thread_malloc(thread, size);
146#endif
147 h->size = h_size;
148
149 h->generation = 0;
150 h->nelements = 0;
151 h->nconflicts = 0;
152 h->buckets = (kmp_dephash_entry **)(h + 1);
153 h->last_all = NULL;
154
155 for (size_t i = 0; i < h_size; i++)
156 h->buckets[i] = 0;
157
158 return h;
159}
160
161static kmp_dephash_entry *__kmp_dephash_find(kmp_info_t *thread,
162 kmp_dephash_t **hash,
163 kmp_intptr_t addr) {
164 kmp_dephash_t *h = *hash;
165 if (h->nelements != 0 && h->nconflicts / h->size >= 1) {
166 *hash = __kmp_dephash_extend(thread, h);
167 h = *hash;
168 }
169 size_t bucket = __kmp_dephash_hash(addr, h->size);
170
171 kmp_dephash_entry_t *entry;
172 for (entry = h->buckets[bucket]; entry; entry = entry->next_in_bucket)
173 if (entry->addr == addr)
174 break;
175
176 if (entry == NULL) {
177// create entry. This is only done by one thread so no locking required
178#if USE_FAST_MEMORY
179 entry = (kmp_dephash_entry_t *)__kmp_fast_allocate(
180 thread, sizeof(kmp_dephash_entry_t));
181#else
182 entry = (kmp_dephash_entry_t *)__kmp_thread_malloc(
183 thread, sizeof(kmp_dephash_entry_t));
184#endif
185 entry->addr = addr;
186 if (!h->last_all) // no predecessor task with omp_all_memory dependence
187 entry->last_out = NULL;
188 else // else link the omp_all_memory depnode to the new entry
189 entry->last_out = __kmp_node_ref(h->last_all);
190 entry->last_set = NULL;
191 entry->prev_set = NULL;
192 entry->last_flag = 0;
193 entry->mtx_lock = NULL;
194 entry->next_in_bucket = h->buckets[bucket];
195 h->buckets[bucket] = entry;
196 h->nelements++;
197 if (entry->next_in_bucket)
198 h->nconflicts++;
199 }
200 return entry;
201}
202
203static kmp_depnode_list_t *__kmp_add_node(kmp_info_t *thread,
204 kmp_depnode_list_t *list,
205 kmp_depnode_t *node) {
206 kmp_depnode_list_t *new_head;
207
208#if USE_FAST_MEMORY
209 new_head = (kmp_depnode_list_t *)__kmp_fast_allocate(
210 thread, sizeof(kmp_depnode_list_t));
211#else
212 new_head = (kmp_depnode_list_t *)__kmp_thread_malloc(
213 thread, sizeof(kmp_depnode_list_t));
214#endif
215
216 new_head->node = __kmp_node_ref(node);
217 new_head->next = list;
218
219 return new_head;
220}
221
222static inline void __kmp_track_dependence(kmp_int32 gtid, kmp_depnode_t *source,
223 kmp_depnode_t *sink,
224 kmp_task_t *sink_task) {
225#ifdef KMP_SUPPORT_GRAPH_OUTPUT
226 kmp_taskdata_t *task_source = KMP_TASK_TO_TASKDATA(source->dn.task);
227 // do not use sink->dn.task as that is only filled after the dependences
228 // are already processed!
229 kmp_taskdata_t *task_sink = KMP_TASK_TO_TASKDATA(sink_task);
230
231 __kmp_printf("%d(%s) -> %d(%s)\n", source->dn.id,
232 task_source->td_ident->psource, sink->dn.id,
233 task_sink->td_ident->psource);
234#endif
235#if OMPT_SUPPORT && OMPT_OPTIONAL
236 /* OMPT tracks dependences between task (a=source, b=sink) in which
237 task a blocks the execution of b through the ompt_new_dependence_callback
238 */
239 if (ompt_enabled.ompt_callback_task_dependence) {
240 kmp_taskdata_t *task_source = KMP_TASK_TO_TASKDATA(source->dn.task);
241 ompt_data_t *sink_data;
242 if (sink_task)
243 sink_data = &(KMP_TASK_TO_TASKDATA(sink_task)->ompt_task_info.task_data);
244 else
245 sink_data = &__kmp_threads[gtid]->th.ompt_thread_info.task_data;
246
247 ompt_callbacks.ompt_callback(ompt_callback_task_dependence)(
248 &(task_source->ompt_task_info.task_data), sink_data);
249 }
250#endif /* OMPT_SUPPORT && OMPT_OPTIONAL */
251}
252
253kmp_base_depnode_t *__kmpc_task_get_depnode(kmp_task_t *task) {
254 kmp_taskdata_t *td = KMP_TASK_TO_TASKDATA(task);
255 return td->td_depnode ? &(td->td_depnode->dn) : NULL;
256}
257
258kmp_depnode_list_t *__kmpc_task_get_successors(kmp_task_t *task) {
259 kmp_taskdata_t *td = KMP_TASK_TO_TASKDATA(task);
260 return td->td_depnode->dn.successors;
261}
262
263static inline kmp_int32
264__kmp_depnode_link_successor(kmp_int32 gtid, kmp_info_t *thread,
265 kmp_task_t *task, kmp_depnode_t *node,
266 kmp_depnode_list_t *plist) {
267 if (!plist)
268 return 0;
269 kmp_int32 npredecessors = 0;
270 // link node as successor of list elements
271 for (kmp_depnode_list_t *p = plist; p; p = p->next) {
272 kmp_depnode_t *dep = p->node;
273 if (dep->dn.task) {
274 KMP_ACQUIRE_DEPNODE(gtid, dep);
275 if (dep->dn.task) {
276 if (!dep->dn.successors || dep->dn.successors->node != node) {
277 __kmp_track_dependence(gtid, dep, node, task);
278 dep->dn.successors = __kmp_add_node(thread, dep->dn.successors, node);
279 KA_TRACE(40, ("__kmp_process_deps: T#%d adding dependence from %p to "
280 "%p\n",
281 gtid, KMP_TASK_TO_TASKDATA(dep->dn.task),
282 KMP_TASK_TO_TASKDATA(task)));
283 npredecessors++;
284 }
285 }
286 KMP_RELEASE_DEPNODE(gtid, dep);
287 }
288 }
289 return npredecessors;
290}
291
292// Add the edge 'sink' -> 'source' in the task dependency graph
293static inline kmp_int32 __kmp_depnode_link_successor(kmp_int32 gtid,
294 kmp_info_t *thread,
295 kmp_task_t *task,
296 kmp_depnode_t *source,
297 kmp_depnode_t *sink) {
298 if (!sink)
299 return 0;
300 kmp_int32 npredecessors = 0;
301 if (sink->dn.task) {
302 // synchronously add source to sink' list of successors
303 KMP_ACQUIRE_DEPNODE(gtid, sink);
304 if (sink->dn.task) {
305 if (!sink->dn.successors || sink->dn.successors->node != source) {
306 __kmp_track_dependence(gtid, sink, source, task);
307 sink->dn.successors = __kmp_add_node(thread, sink->dn.successors, source);
308 KA_TRACE(40, ("__kmp_process_deps: T#%d adding dependence from %p to "
309 "%p\n",
310 gtid, KMP_TASK_TO_TASKDATA(sink->dn.task),
311 KMP_TASK_TO_TASKDATA(task)));
312 npredecessors++;
313 }
314 }
315 KMP_RELEASE_DEPNODE(gtid, sink);
316 }
317 return npredecessors;
318}
319
320static inline kmp_int32
321__kmp_process_dep_all(kmp_int32 gtid, kmp_depnode_t *node, kmp_dephash_t *h,
322 bool dep_barrier, kmp_task_t *task) {
323 KA_TRACE(30, ("__kmp_process_dep_all: T#%d processing dep_all, "
324 "dep_barrier = %d\n",
325 gtid, dep_barrier));
326 kmp_info_t *thread = __kmp_threads[gtid];
327 kmp_int32 npredecessors = 0;
328
329 // process previous omp_all_memory node if any
330 npredecessors +=
331 __kmp_depnode_link_successor(gtid, thread, task, node, h->last_all);
332 __kmp_node_deref(thread, h->last_all);
333 if (!dep_barrier) {
334 h->last_all = __kmp_node_ref(node);
335 } else {
336 // if this is a sync point in the serial sequence, then the previous
337 // outputs are guaranteed to be completed after the execution of this
338 // task so the previous output nodes can be cleared.
339 h->last_all = NULL;
340 }
341
342 // process all regular dependences
343 for (size_t i = 0; i < h->size; i++) {
344 kmp_dephash_entry_t *info = h->buckets[i];
345 if (!info) // skip empty slots in dephash
346 continue;
347 for (; info; info = info->next_in_bucket) {
348 // for each entry the omp_all_memory works as OUT dependence
349 kmp_depnode_t *last_out = info->last_out;
350 kmp_depnode_list_t *last_set = info->last_set;
351 kmp_depnode_list_t *prev_set = info->prev_set;
352 if (last_set) {
353 npredecessors +=
354 __kmp_depnode_link_successor(gtid, thread, task, node, last_set);
355 __kmp_depnode_list_free(thread, last_set);
356 __kmp_depnode_list_free(thread, prev_set);
357 info->last_set = NULL;
358 info->prev_set = NULL;
359 info->last_flag = 0; // no sets in this dephash entry
360 } else {
361 npredecessors +=
362 __kmp_depnode_link_successor(gtid, thread, task, node, last_out);
363 }
364 __kmp_node_deref(thread, last_out);
365 if (!dep_barrier) {
366 info->last_out = __kmp_node_ref(node);
367 } else {
368 info->last_out = NULL;
369 }
370 }
371 }
372 KA_TRACE(30, ("__kmp_process_dep_all: T#%d found %d predecessors\n", gtid,
373 npredecessors));
374 return npredecessors;
375}
376
377template <bool filter>
378static inline kmp_int32
379__kmp_process_deps(kmp_int32 gtid, kmp_depnode_t *node, kmp_dephash_t **hash,
380 bool dep_barrier, kmp_int32 ndeps,
381 kmp_depend_info_t *dep_list, kmp_task_t *task) {
382 KA_TRACE(30, ("__kmp_process_deps<%d>: T#%d processing %d dependences : "
383 "dep_barrier = %d\n",
384 filter, gtid, ndeps, dep_barrier));
385
386 kmp_info_t *thread = __kmp_threads[gtid];
387 kmp_int32 npredecessors = 0;
388 for (kmp_int32 i = 0; i < ndeps; i++) {
389 const kmp_depend_info_t *dep = &dep_list[i];
390
391 if (filter && dep->base_addr == 0)
392 continue; // skip filtered entries
393
394 kmp_dephash_entry_t *info =
395 __kmp_dephash_find(thread, hash, dep->base_addr);
396 kmp_depnode_t *last_out = info->last_out;
397 kmp_depnode_list_t *last_set = info->last_set;
398 kmp_depnode_list_t *prev_set = info->prev_set;
399
400 if (dep->flags.out) { // out or inout --> clean lists if any
401 if (last_set) {
402 npredecessors +=
403 __kmp_depnode_link_successor(gtid, thread, task, node, last_set);
404 __kmp_depnode_list_free(thread, last_set);
405 __kmp_depnode_list_free(thread, prev_set);
406 info->last_set = NULL;
407 info->prev_set = NULL;
408 info->last_flag = 0; // no sets in this dephash entry
409 } else {
410 npredecessors +=
411 __kmp_depnode_link_successor(gtid, thread, task, node, last_out);
412 }
413 __kmp_node_deref(thread, last_out);
414 if (!dep_barrier) {
415 info->last_out = __kmp_node_ref(node);
416 } else {
417 // if this is a sync point in the serial sequence, then the previous
418 // outputs are guaranteed to be completed after the execution of this
419 // task so the previous output nodes can be cleared.
420 info->last_out = NULL;
421 }
422 } else { // either IN or MTX or SET
423 if (info->last_flag == 0 || info->last_flag == dep->flag) {
424 // last_set either didn't exist or of same dep kind
425 // link node as successor of the last_out if any
426 npredecessors +=
427 __kmp_depnode_link_successor(gtid, thread, task, node, last_out);
428 // link node as successor of all nodes in the prev_set if any
429 npredecessors +=
430 __kmp_depnode_link_successor(gtid, thread, task, node, prev_set);
431 if (dep_barrier) {
432 // clean last_out and prev_set if any; don't touch last_set
433 __kmp_node_deref(thread, last_out);
434 info->last_out = NULL;
435 __kmp_depnode_list_free(thread, prev_set);
436 info->prev_set = NULL;
437 }
438 } else { // last_set is of different dep kind, make it prev_set
439 // link node as successor of all nodes in the last_set
440 npredecessors +=
441 __kmp_depnode_link_successor(gtid, thread, task, node, last_set);
442 // clean last_out if any
443 __kmp_node_deref(thread, last_out);
444 info->last_out = NULL;
445 // clean prev_set if any
446 __kmp_depnode_list_free(thread, prev_set);
447 if (!dep_barrier) {
448 // move last_set to prev_set, new last_set will be allocated
449 info->prev_set = last_set;
450 } else {
451 info->prev_set = NULL;
452 info->last_flag = 0;
453 }
454 info->last_set = NULL;
455 }
456 // for dep_barrier last_flag value should remain:
457 // 0 if last_set is empty, unchanged otherwise
458 if (!dep_barrier) {
459 info->last_flag = dep->flag; // store dep kind of the last_set
460 info->last_set = __kmp_add_node(thread, info->last_set, node);
461 }
462 // check if we are processing MTX dependency
463 if (dep->flag == KMP_DEP_MTX) {
464 if (info->mtx_lock == NULL) {
465 info->mtx_lock = (kmp_lock_t *)__kmp_allocate(sizeof(kmp_lock_t));
466 __kmp_init_lock(info->mtx_lock);
467 }
468 KMP_DEBUG_ASSERT(node->dn.mtx_num_locks < MAX_MTX_DEPS);
469 kmp_int32 m;
470 // Save lock in node's array
471 for (m = 0; m < MAX_MTX_DEPS; ++m) {
472 // sort pointers in decreasing order to avoid potential livelock
473 if (node->dn.mtx_locks[m] < info->mtx_lock) {
474 KMP_DEBUG_ASSERT(!node->dn.mtx_locks[node->dn.mtx_num_locks]);
475 for (int n = node->dn.mtx_num_locks; n > m; --n) {
476 // shift right all lesser non-NULL pointers
477 KMP_DEBUG_ASSERT(node->dn.mtx_locks[n - 1] != NULL);
478 node->dn.mtx_locks[n] = node->dn.mtx_locks[n - 1];
479 }
480 node->dn.mtx_locks[m] = info->mtx_lock;
481 break;
482 }
483 }
484 KMP_DEBUG_ASSERT(m < MAX_MTX_DEPS); // must break from loop
485 node->dn.mtx_num_locks++;
486 }
487 }
488 }
489 KA_TRACE(30, ("__kmp_process_deps<%d>: T#%d found %d predecessors\n", filter,
490 gtid, npredecessors));
491 return npredecessors;
492}
493
494#define NO_DEP_BARRIER (false)
495#define DEP_BARRIER (true)
496
497// returns true if the task has any outstanding dependence
498static bool __kmp_check_deps(kmp_int32 gtid, kmp_depnode_t *node,
499 kmp_task_t *task, kmp_dephash_t **hash,
500 bool dep_barrier, kmp_int32 ndeps,
501 kmp_depend_info_t *dep_list,
502 kmp_int32 ndeps_noalias,
503 kmp_depend_info_t *noalias_dep_list) {
504 int i, n_mtxs = 0, dep_all = 0;
505#if KMP_DEBUG
506 kmp_taskdata_t *taskdata = KMP_TASK_TO_TASKDATA(task);
507#endif
508 KA_TRACE(20, ("__kmp_check_deps: T#%d checking dependences for task %p : %d "
509 "possibly aliased dependences, %d non-aliased dependences : "
510 "dep_barrier=%d .\n",
511 gtid, taskdata, ndeps, ndeps_noalias, dep_barrier));
512
513 // Filter deps in dep_list
514 // TODO: Different algorithm for large dep_list ( > 10 ? )
515 for (i = 0; i < ndeps; i++) {
516 if (dep_list[i].base_addr != 0 &&
517 dep_list[i].base_addr != (kmp_intptr_t)KMP_SIZE_T_MAX) {
518 KMP_DEBUG_ASSERT(
519 dep_list[i].flag == KMP_DEP_IN || dep_list[i].flag == KMP_DEP_OUT ||
520 dep_list[i].flag == KMP_DEP_INOUT ||
521 dep_list[i].flag == KMP_DEP_MTX || dep_list[i].flag == KMP_DEP_SET);
522 for (int j = i + 1; j < ndeps; j++) {
523 if (dep_list[i].base_addr == dep_list[j].base_addr) {
524 if (dep_list[i].flag != dep_list[j].flag) {
525 // two different dependences on same address work identical to OUT
526 dep_list[i].flag = KMP_DEP_OUT;
527 }
528 dep_list[j].base_addr = 0; // Mark j element as void
529 }
530 }
531 if (dep_list[i].flag == KMP_DEP_MTX) {
532 // limit number of mtx deps to MAX_MTX_DEPS per node
533 if (n_mtxs < MAX_MTX_DEPS && task != NULL) {
534 ++n_mtxs;
535 } else {
536 dep_list[i].flag = KMP_DEP_OUT; // downgrade mutexinoutset to inout
537 }
538 }
539 } else if (dep_list[i].flag == KMP_DEP_ALL ||
540 dep_list[i].base_addr == (kmp_intptr_t)KMP_SIZE_T_MAX) {
541 // omp_all_memory dependence can be marked by compiler by either
542 // (addr=0 && flag=0x80) (flag KMP_DEP_ALL), or (addr=-1).
543 // omp_all_memory overrides all other dependences if any
544 dep_all = 1;
545 break;
546 }
547 }
548
549 // doesn't need to be atomic as no other thread is going to be accessing this
550 // node just yet.
551 // npredecessors is set -1 to ensure that none of the releasing tasks queues
552 // this task before we have finished processing all the dependences
553 node->dn.npredecessors = -1;
554
555 // used to pack all npredecessors additions into a single atomic operation at
556 // the end
557 int npredecessors;
558
559 if (!dep_all) { // regular dependences
560 npredecessors = __kmp_process_deps<true>(gtid, node, hash, dep_barrier,
561 ndeps, dep_list, task);
562 npredecessors += __kmp_process_deps<false>(
563 gtid, node, hash, dep_barrier, ndeps_noalias, noalias_dep_list, task);
564 } else { // omp_all_memory dependence
565 npredecessors = __kmp_process_dep_all(gtid, node, *hash, dep_barrier, task);
566 }
567
568 node->dn.task = task;
569 KMP_MB();
570
571 // Account for our initial fake value
572 npredecessors++;
573
574 // Update predecessors and obtain current value to check if there are still
575 // any outstanding dependences (some tasks may have finished while we
576 // processed the dependences)
577 npredecessors =
578 node->dn.npredecessors.fetch_add(npredecessors) + npredecessors;
579
580 KA_TRACE(20, ("__kmp_check_deps: T#%d found %d predecessors for task %p \n",
581 gtid, npredecessors, taskdata));
582
583 // beyond this point the task could be queued (and executed) by a releasing
584 // task...
585 return npredecessors > 0 ? true : false;
586}
587
604kmp_int32 __kmpc_omp_task_with_deps(ident_t *loc_ref, kmp_int32 gtid,
605 kmp_task_t *new_task, kmp_int32 ndeps,
606 kmp_depend_info_t *dep_list,
607 kmp_int32 ndeps_noalias,
608 kmp_depend_info_t *noalias_dep_list) {
609
610 kmp_taskdata_t *new_taskdata = KMP_TASK_TO_TASKDATA(new_task);
611 KA_TRACE(10, ("__kmpc_omp_task_with_deps(enter): T#%d loc=%p task=%p\n", gtid,
612 loc_ref, new_taskdata));
613 __kmp_assert_valid_gtid(gtid);
614 kmp_info_t *thread = __kmp_threads[gtid];
615 kmp_taskdata_t *current_task = thread->th.th_current_task;
616
617#if OMPT_SUPPORT
618 if (ompt_enabled.enabled) {
619 if (!current_task->ompt_task_info.frame.enter_frame.ptr)
620 current_task->ompt_task_info.frame.enter_frame.ptr =
621 OMPT_GET_FRAME_ADDRESS(0);
622 if (ompt_enabled.ompt_callback_task_create) {
623 ompt_callbacks.ompt_callback(ompt_callback_task_create)(
624 &(current_task->ompt_task_info.task_data),
625 &(current_task->ompt_task_info.frame),
626 &(new_taskdata->ompt_task_info.task_data),
627 TASK_TYPE_DETAILS_FORMAT(new_taskdata), 1,
628 OMPT_LOAD_OR_GET_RETURN_ADDRESS(gtid));
629 }
630
631 new_taskdata->ompt_task_info.frame.enter_frame.ptr =
632 OMPT_GET_FRAME_ADDRESS(0);
633 }
634
635#if OMPT_OPTIONAL
636 /* OMPT grab all dependences if requested by the tool */
637 if (ndeps + ndeps_noalias > 0 && ompt_enabled.ompt_callback_dependences) {
638 kmp_int32 i;
639
640 int ompt_ndeps = ndeps + ndeps_noalias;
641 ompt_dependence_t *ompt_deps = (ompt_dependence_t *)KMP_OMPT_DEPS_ALLOC(
642 thread, (ndeps + ndeps_noalias) * sizeof(ompt_dependence_t));
643
644 KMP_ASSERT(ompt_deps != NULL);
645
646 for (i = 0; i < ndeps; i++) {
647 ompt_deps[i].variable.ptr = (void *)dep_list[i].base_addr;
648 if (dep_list[i].base_addr == (kmp_intptr_t)KMP_SIZE_T_MAX)
649 ompt_deps[i].dependence_type = ompt_dependence_type_out_all_memory;
650 else if (dep_list[i].flags.in && dep_list[i].flags.out)
651 ompt_deps[i].dependence_type = ompt_dependence_type_inout;
652 else if (dep_list[i].flags.out)
653 ompt_deps[i].dependence_type = ompt_dependence_type_out;
654 else if (dep_list[i].flags.in)
655 ompt_deps[i].dependence_type = ompt_dependence_type_in;
656 else if (dep_list[i].flags.mtx)
657 ompt_deps[i].dependence_type = ompt_dependence_type_mutexinoutset;
658 else if (dep_list[i].flags.set)
659 ompt_deps[i].dependence_type = ompt_dependence_type_inoutset;
660 else if (dep_list[i].flags.all)
661 ompt_deps[i].dependence_type = ompt_dependence_type_out_all_memory;
662 }
663 for (i = 0; i < ndeps_noalias; i++) {
664 ompt_deps[ndeps + i].variable.ptr = (void *)noalias_dep_list[i].base_addr;
665 if (noalias_dep_list[i].base_addr == (kmp_intptr_t)KMP_SIZE_T_MAX)
666 ompt_deps[ndeps + i].dependence_type =
667 ompt_dependence_type_out_all_memory;
668 else if (noalias_dep_list[i].flags.in && noalias_dep_list[i].flags.out)
669 ompt_deps[ndeps + i].dependence_type = ompt_dependence_type_inout;
670 else if (noalias_dep_list[i].flags.out)
671 ompt_deps[ndeps + i].dependence_type = ompt_dependence_type_out;
672 else if (noalias_dep_list[i].flags.in)
673 ompt_deps[ndeps + i].dependence_type = ompt_dependence_type_in;
674 else if (noalias_dep_list[i].flags.mtx)
675 ompt_deps[ndeps + i].dependence_type =
676 ompt_dependence_type_mutexinoutset;
677 else if (noalias_dep_list[i].flags.set)
678 ompt_deps[ndeps + i].dependence_type = ompt_dependence_type_inoutset;
679 else if (noalias_dep_list[i].flags.all)
680 ompt_deps[ndeps + i].dependence_type =
681 ompt_dependence_type_out_all_memory;
682 }
683 ompt_callbacks.ompt_callback(ompt_callback_dependences)(
684 &(new_taskdata->ompt_task_info.task_data), ompt_deps, ompt_ndeps);
685 /* We can now free the allocated memory for the dependences */
686 /* For OMPD we might want to delay the free until end of this function */
687 KMP_OMPT_DEPS_FREE(thread, ompt_deps);
688 }
689#endif /* OMPT_OPTIONAL */
690#endif /* OMPT_SUPPORT */
691
692 bool serial = current_task->td_flags.team_serial ||
693 current_task->td_flags.tasking_ser ||
694 current_task->td_flags.final;
695 kmp_task_team_t *task_team = thread->th.th_task_team;
696 serial = serial &&
697 !(task_team && (task_team->tt.tt_found_proxy_tasks ||
698 task_team->tt.tt_hidden_helper_task_encountered));
699
700 if (!serial && (ndeps > 0 || ndeps_noalias > 0)) {
701 /* if no dependences have been tracked yet, create the dependence hash */
702 if (current_task->td_dephash == NULL)
703 current_task->td_dephash = __kmp_dephash_create(thread, current_task);
704
705#if USE_FAST_MEMORY
706 kmp_depnode_t *node =
707 (kmp_depnode_t *)__kmp_fast_allocate(thread, sizeof(kmp_depnode_t));
708#else
709 kmp_depnode_t *node =
710 (kmp_depnode_t *)__kmp_thread_malloc(thread, sizeof(kmp_depnode_t));
711#endif
712
713 __kmp_init_node(node, /*on_stack=*/false);
714 new_taskdata->td_depnode = node;
715
716 if (__kmp_check_deps(gtid, node, new_task, &current_task->td_dephash,
717 NO_DEP_BARRIER, ndeps, dep_list, ndeps_noalias,
718 noalias_dep_list)) {
719 KA_TRACE(10, ("__kmpc_omp_task_with_deps(exit): T#%d task had blocking "
720 "dependences: "
721 "loc=%p task=%p, return: TASK_CURRENT_NOT_QUEUED\n",
722 gtid, loc_ref, new_taskdata));
723#if OMPT_SUPPORT
724 if (ompt_enabled.enabled) {
725 current_task->ompt_task_info.frame.enter_frame = ompt_data_none;
726 }
727#endif
728 return TASK_CURRENT_NOT_QUEUED;
729 }
730 } else {
731 KA_TRACE(10, ("__kmpc_omp_task_with_deps(exit): T#%d ignored dependences "
732 "for task (serialized) loc=%p task=%p\n",
733 gtid, loc_ref, new_taskdata));
734 }
735
736 KA_TRACE(10, ("__kmpc_omp_task_with_deps(exit): T#%d task had no blocking "
737 "dependences : "
738 "loc=%p task=%p, transferring to __kmp_omp_task\n",
739 gtid, loc_ref, new_taskdata));
740
741 kmp_int32 ret = __kmp_omp_task(gtid, new_task, true);
742#if OMPT_SUPPORT
743 if (ompt_enabled.enabled) {
744 current_task->ompt_task_info.frame.enter_frame = ompt_data_none;
745 }
746#endif
747 return ret;
748}
749
750#if OMPT_SUPPORT
751void __ompt_taskwait_dep_finish(kmp_taskdata_t *current_task,
752 ompt_data_t *taskwait_task_data) {
753 if (ompt_enabled.ompt_callback_task_schedule) {
754 ompt_callbacks.ompt_callback(ompt_callback_task_schedule)(
755 taskwait_task_data, ompt_taskwait_complete, NULL);
756 }
757 current_task->ompt_task_info.frame.enter_frame.ptr = NULL;
758 *taskwait_task_data = ompt_data_none;
759}
760#endif /* OMPT_SUPPORT */
761
773void __kmpc_omp_wait_deps(ident_t *loc_ref, kmp_int32 gtid, kmp_int32 ndeps,
774 kmp_depend_info_t *dep_list, kmp_int32 ndeps_noalias,
775 kmp_depend_info_t *noalias_dep_list) {
776 __kmpc_omp_taskwait_deps_51(loc_ref, gtid, ndeps, dep_list, ndeps_noalias,
777 noalias_dep_list, false);
778}
779
780/* __kmpc_omp_taskwait_deps_51 : Function for OpenMP 5.1 nowait clause.
781 Placeholder for taskwait with nowait clause.
782 Earlier code of __kmpc_omp_wait_deps() is now
783 in this function.
784*/
785void __kmpc_omp_taskwait_deps_51(ident_t *loc_ref, kmp_int32 gtid,
786 kmp_int32 ndeps, kmp_depend_info_t *dep_list,
787 kmp_int32 ndeps_noalias,
788 kmp_depend_info_t *noalias_dep_list,
789 kmp_int32 has_no_wait) {
790 KA_TRACE(10, ("__kmpc_omp_taskwait_deps(enter): T#%d loc=%p nowait#%d\n",
791 gtid, loc_ref, has_no_wait));
792 if (ndeps == 0 && ndeps_noalias == 0) {
793 KA_TRACE(10, ("__kmpc_omp_taskwait_deps(exit): T#%d has no dependences to "
794 "wait upon : loc=%p\n",
795 gtid, loc_ref));
796 return;
797 }
798 __kmp_assert_valid_gtid(gtid);
799 kmp_info_t *thread = __kmp_threads[gtid];
800 kmp_taskdata_t *current_task = thread->th.th_current_task;
801
802#if OMPT_SUPPORT
803 // this function represents a taskwait construct with depend clause
804 // We signal 4 events:
805 // - creation of the taskwait task
806 // - dependences of the taskwait task
807 // - schedule and finish of the taskwait task
808 ompt_data_t *taskwait_task_data = &thread->th.ompt_thread_info.task_data;
809 KMP_ASSERT(taskwait_task_data->ptr == NULL);
810 if (ompt_enabled.enabled) {
811 if (!current_task->ompt_task_info.frame.enter_frame.ptr)
812 current_task->ompt_task_info.frame.enter_frame.ptr =
813 OMPT_GET_FRAME_ADDRESS(0);
814 if (ompt_enabled.ompt_callback_task_create) {
815 ompt_callbacks.ompt_callback(ompt_callback_task_create)(
816 &(current_task->ompt_task_info.task_data),
817 &(current_task->ompt_task_info.frame), taskwait_task_data,
818 ompt_task_taskwait | ompt_task_undeferred | ompt_task_mergeable, 1,
819 OMPT_LOAD_OR_GET_RETURN_ADDRESS(gtid));
820 }
821 }
822
823#if OMPT_OPTIONAL
824 /* OMPT grab all dependences if requested by the tool */
825 if (ndeps + ndeps_noalias > 0 && ompt_enabled.ompt_callback_dependences) {
826 kmp_int32 i;
827
828 int ompt_ndeps = ndeps + ndeps_noalias;
829 ompt_dependence_t *ompt_deps = (ompt_dependence_t *)KMP_OMPT_DEPS_ALLOC(
830 thread, (ndeps + ndeps_noalias) * sizeof(ompt_dependence_t));
831
832 KMP_ASSERT(ompt_deps != NULL);
833
834 for (i = 0; i < ndeps; i++) {
835 ompt_deps[i].variable.ptr = (void *)dep_list[i].base_addr;
836 if (dep_list[i].flags.in && dep_list[i].flags.out)
837 ompt_deps[i].dependence_type = ompt_dependence_type_inout;
838 else if (dep_list[i].flags.out)
839 ompt_deps[i].dependence_type = ompt_dependence_type_out;
840 else if (dep_list[i].flags.in)
841 ompt_deps[i].dependence_type = ompt_dependence_type_in;
842 else if (dep_list[i].flags.mtx)
843 ompt_deps[ndeps + i].dependence_type =
844 ompt_dependence_type_mutexinoutset;
845 else if (dep_list[i].flags.set)
846 ompt_deps[ndeps + i].dependence_type = ompt_dependence_type_inoutset;
847 }
848 for (i = 0; i < ndeps_noalias; i++) {
849 ompt_deps[ndeps + i].variable.ptr = (void *)noalias_dep_list[i].base_addr;
850 if (noalias_dep_list[i].flags.in && noalias_dep_list[i].flags.out)
851 ompt_deps[ndeps + i].dependence_type = ompt_dependence_type_inout;
852 else if (noalias_dep_list[i].flags.out)
853 ompt_deps[ndeps + i].dependence_type = ompt_dependence_type_out;
854 else if (noalias_dep_list[i].flags.in)
855 ompt_deps[ndeps + i].dependence_type = ompt_dependence_type_in;
856 else if (noalias_dep_list[i].flags.mtx)
857 ompt_deps[ndeps + i].dependence_type =
858 ompt_dependence_type_mutexinoutset;
859 else if (noalias_dep_list[i].flags.set)
860 ompt_deps[ndeps + i].dependence_type = ompt_dependence_type_inoutset;
861 }
862 ompt_callbacks.ompt_callback(ompt_callback_dependences)(
863 taskwait_task_data, ompt_deps, ompt_ndeps);
864 /* We can now free the allocated memory for the dependences */
865 /* For OMPD we might want to delay the free until end of this function */
866 KMP_OMPT_DEPS_FREE(thread, ompt_deps);
867 ompt_deps = NULL;
868 }
869#endif /* OMPT_OPTIONAL */
870#endif /* OMPT_SUPPORT */
871
872 // We can return immediately as:
873 // - dependences are not computed in serial teams (except with proxy tasks)
874 // - if the dephash is not yet created it means we have nothing to wait for
875 bool ignore = current_task->td_flags.team_serial ||
876 current_task->td_flags.tasking_ser ||
877 current_task->td_flags.final;
878 ignore =
879 ignore && thread->th.th_task_team != NULL &&
880 thread->th.th_task_team->tt.tt_found_proxy_tasks == FALSE &&
881 thread->th.th_task_team->tt.tt_hidden_helper_task_encountered == FALSE;
882 ignore = ignore || current_task->td_dephash == NULL;
883
884 if (ignore) {
885 KA_TRACE(10, ("__kmpc_omp_taskwait_deps(exit): T#%d has no blocking "
886 "dependences : loc=%p\n",
887 gtid, loc_ref));
888#if OMPT_SUPPORT
889 __ompt_taskwait_dep_finish(current_task, taskwait_task_data);
890#endif /* OMPT_SUPPORT */
891 return;
892 }
893
894 kmp_depnode_t node = {0};
895 __kmp_init_node(&node, /*on_stack=*/true);
896
897 if (!__kmp_check_deps(gtid, &node, NULL, &current_task->td_dephash,
898 DEP_BARRIER, ndeps, dep_list, ndeps_noalias,
899 noalias_dep_list)) {
900 KA_TRACE(10, ("__kmpc_omp_taskwait_deps(exit): T#%d has no blocking "
901 "dependences : loc=%p\n",
902 gtid, loc_ref));
903#if OMPT_SUPPORT
904 __ompt_taskwait_dep_finish(current_task, taskwait_task_data);
905#endif /* OMPT_SUPPORT */
906
907 // There may still be references to this node here, due to task stealing.
908 // Wait for them to be released.
909 kmp_int32 nrefs;
910 while ((nrefs = node.dn.nrefs) > 3) {
911 KMP_DEBUG_ASSERT((nrefs & 1) == 1);
912 KMP_YIELD(TRUE);
913 }
914 KMP_DEBUG_ASSERT(nrefs == 3);
915
916 return;
917 }
918
919 int thread_finished = FALSE;
920 kmp_flag_32<false, false> flag(
921 (std::atomic<kmp_uint32> *)&node.dn.npredecessors, 0U);
922 while (node.dn.npredecessors > 0) {
923 flag.execute_tasks(thread, gtid, FALSE,
924 &thread_finished USE_ITT_BUILD_ARG(NULL),
925 __kmp_task_stealing_constraint);
926 }
927
928 // Wait until the last __kmp_release_deps is finished before we free the
929 // current stack frame holding the "node" variable; once its nrefs count
930 // reaches 3 (meaning 1, since bit zero of the refcount indicates a stack
931 // rather than a heap address), we're sure nobody else can try to reference
932 // it again.
933 kmp_int32 nrefs;
934 while ((nrefs = node.dn.nrefs) > 3) {
935 KMP_DEBUG_ASSERT((nrefs & 1) == 1);
936 KMP_YIELD(TRUE);
937 }
938 KMP_DEBUG_ASSERT(nrefs == 3);
939
940#if OMPT_SUPPORT
941 __ompt_taskwait_dep_finish(current_task, taskwait_task_data);
942#endif /* OMPT_SUPPORT */
943 KA_TRACE(10, ("__kmpc_omp_taskwait_deps(exit): T#%d finished waiting : loc=%p\
944 \n",
945 gtid, loc_ref));
946}
kmp_int32 __kmpc_omp_task_with_deps(ident_t *loc_ref, kmp_int32 gtid, kmp_task_t *new_task, kmp_int32 ndeps, kmp_depend_info_t *dep_list, kmp_int32 ndeps_noalias, kmp_depend_info_t *noalias_dep_list)
void __kmpc_omp_wait_deps(ident_t *loc_ref, kmp_int32 gtid, kmp_int32 ndeps, kmp_depend_info_t *dep_list, kmp_int32 ndeps_noalias, kmp_depend_info_t *noalias_dep_list)
Definition: kmp.h:217