LLVM OpenMP* Runtime Library
kmp_barrier.cpp
1 /*
2  * kmp_barrier.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 #include "kmp.h"
14 #include "kmp_wait_release.h"
15 #include "kmp_itt.h"
16 #include "kmp_os.h"
17 #include "kmp_stats.h"
18 #include "ompt-specific.h"
19 
20 #if KMP_MIC
21 #include <immintrin.h>
22 #define USE_NGO_STORES 1
23 #endif // KMP_MIC
24 
25 #if KMP_MIC && USE_NGO_STORES
26 // ICV copying
27 #define ngo_load(src) __m512d Vt = _mm512_load_pd((void *)(src))
28 #define ngo_store_icvs(dst, src) _mm512_storenrngo_pd((void *)(dst), Vt)
29 #define ngo_store_go(dst, src) _mm512_storenrngo_pd((void *)(dst), Vt)
30 #define ngo_sync() __asm__ volatile("lock; addl $0,0(%%rsp)" ::: "memory")
31 #else
32 #define ngo_load(src) ((void)0)
33 #define ngo_store_icvs(dst, src) copy_icvs((dst), (src))
34 #define ngo_store_go(dst, src) KMP_MEMCPY((dst), (src), CACHE_LINE)
35 #define ngo_sync() ((void)0)
36 #endif /* KMP_MIC && USE_NGO_STORES */
37 
38 void __kmp_print_structure(void); // Forward declaration
39 
40 // ---------------------------- Barrier Algorithms ----------------------------
41 
42 // Linear Barrier
43 template <bool cancellable = false>
44 static bool __kmp_linear_barrier_gather_template(
45  enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
46  void (*reduce)(void *, void *) USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
47  KMP_TIME_DEVELOPER_PARTITIONED_BLOCK(KMP_linear_gather);
48  kmp_team_t *team = this_thr->th.th_team;
49  kmp_bstate_t *thr_bar = &this_thr->th.th_bar[bt].bb;
50  kmp_info_t **other_threads = team->t.t_threads;
51 
52  KA_TRACE(
53  20,
54  ("__kmp_linear_barrier_gather: T#%d(%d:%d) enter for barrier type %d\n",
55  gtid, team->t.t_id, tid, bt));
56  KMP_DEBUG_ASSERT(this_thr == other_threads[this_thr->th.th_info.ds.ds_tid]);
57 
58 #if USE_ITT_BUILD && USE_ITT_NOTIFY
59  // Barrier imbalance - save arrive time to the thread
60  if (__kmp_forkjoin_frames_mode == 3 || __kmp_forkjoin_frames_mode == 2) {
61  this_thr->th.th_bar_arrive_time = this_thr->th.th_bar_min_time =
62  __itt_get_timestamp();
63  }
64 #endif
65  // We now perform a linear reduction to signal that all of the threads have
66  // arrived.
67  if (!KMP_MASTER_TID(tid)) {
68  KA_TRACE(20,
69  ("__kmp_linear_barrier_gather: T#%d(%d:%d) releasing T#%d(%d:%d)"
70  "arrived(%p): %llu => %llu\n",
71  gtid, team->t.t_id, tid, __kmp_gtid_from_tid(0, team),
72  team->t.t_id, 0, &thr_bar->b_arrived, thr_bar->b_arrived,
73  thr_bar->b_arrived + KMP_BARRIER_STATE_BUMP));
74  // Mark arrival to primary thread
75  /* After performing this write, a worker thread may not assume that the team
76  is valid any more - it could be deallocated by the primary thread at any
77  time. */
78  kmp_flag_64<> flag(&thr_bar->b_arrived, other_threads[0]);
79  flag.release();
80  } else {
81  kmp_balign_team_t *team_bar = &team->t.t_bar[bt];
82  int nproc = this_thr->th.th_team_nproc;
83  int i;
84  // Don't have to worry about sleep bit here or atomic since team setting
85  kmp_uint64 new_state = team_bar->b_arrived + KMP_BARRIER_STATE_BUMP;
86 
87  // Collect all the worker team member threads.
88  for (i = 1; i < nproc; ++i) {
89 #if KMP_CACHE_MANAGE
90  // Prefetch next thread's arrived count
91  if (i + 1 < nproc)
92  KMP_CACHE_PREFETCH(&other_threads[i + 1]->th.th_bar[bt].bb.b_arrived);
93 #endif /* KMP_CACHE_MANAGE */
94  KA_TRACE(20, ("__kmp_linear_barrier_gather: T#%d(%d:%d) wait T#%d(%d:%d) "
95  "arrived(%p) == %llu\n",
96  gtid, team->t.t_id, tid, __kmp_gtid_from_tid(i, team),
97  team->t.t_id, i,
98  &other_threads[i]->th.th_bar[bt].bb.b_arrived, new_state));
99 
100  // Wait for worker thread to arrive
101  if (cancellable) {
102  kmp_flag_64<true, false> flag(
103  &other_threads[i]->th.th_bar[bt].bb.b_arrived, new_state);
104  if (flag.wait(this_thr, FALSE USE_ITT_BUILD_ARG(itt_sync_obj)))
105  return true;
106  } else {
107  kmp_flag_64<> flag(&other_threads[i]->th.th_bar[bt].bb.b_arrived,
108  new_state);
109  flag.wait(this_thr, FALSE USE_ITT_BUILD_ARG(itt_sync_obj));
110  }
111 #if USE_ITT_BUILD && USE_ITT_NOTIFY
112  // Barrier imbalance - write min of the thread time and the other thread
113  // time to the thread.
114  if (__kmp_forkjoin_frames_mode == 2) {
115  this_thr->th.th_bar_min_time = KMP_MIN(
116  this_thr->th.th_bar_min_time, other_threads[i]->th.th_bar_min_time);
117  }
118 #endif
119  if (reduce) {
120  KA_TRACE(100,
121  ("__kmp_linear_barrier_gather: T#%d(%d:%d) += T#%d(%d:%d)\n",
122  gtid, team->t.t_id, tid, __kmp_gtid_from_tid(i, team),
123  team->t.t_id, i));
124  OMPT_REDUCTION_DECL(this_thr, gtid);
125  OMPT_REDUCTION_BEGIN;
126  (*reduce)(this_thr->th.th_local.reduce_data,
127  other_threads[i]->th.th_local.reduce_data);
128  OMPT_REDUCTION_END;
129  }
130  }
131  // Don't have to worry about sleep bit here or atomic since team setting
132  team_bar->b_arrived = new_state;
133  KA_TRACE(20, ("__kmp_linear_barrier_gather: T#%d(%d:%d) set team %d "
134  "arrived(%p) = %llu\n",
135  gtid, team->t.t_id, tid, team->t.t_id, &team_bar->b_arrived,
136  new_state));
137  }
138  KA_TRACE(
139  20,
140  ("__kmp_linear_barrier_gather: T#%d(%d:%d) exit for barrier type %d\n",
141  gtid, team->t.t_id, tid, bt));
142  return false;
143 }
144 
145 template <bool cancellable = false>
146 static bool __kmp_linear_barrier_release_template(
147  enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
148  int propagate_icvs USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
149  KMP_TIME_DEVELOPER_PARTITIONED_BLOCK(KMP_linear_release);
150  kmp_bstate_t *thr_bar = &this_thr->th.th_bar[bt].bb;
151  kmp_team_t *team;
152 
153  if (KMP_MASTER_TID(tid)) {
154  unsigned int i;
155  kmp_uint32 nproc = this_thr->th.th_team_nproc;
156  kmp_info_t **other_threads;
157 
158  team = __kmp_threads[gtid]->th.th_team;
159  KMP_DEBUG_ASSERT(team != NULL);
160  other_threads = team->t.t_threads;
161 
162  KA_TRACE(20, ("__kmp_linear_barrier_release: T#%d(%d:%d) primary enter for "
163  "barrier type %d\n",
164  gtid, team->t.t_id, tid, bt));
165 
166  if (nproc > 1) {
167 #if KMP_BARRIER_ICV_PUSH
168  {
169  KMP_TIME_DEVELOPER_PARTITIONED_BLOCK(USER_icv_copy);
170  if (propagate_icvs) {
171  ngo_load(&team->t.t_implicit_task_taskdata[0].td_icvs);
172  for (i = 1; i < nproc; ++i) {
173  __kmp_init_implicit_task(team->t.t_ident, team->t.t_threads[i],
174  team, i, FALSE);
175  ngo_store_icvs(&team->t.t_implicit_task_taskdata[i].td_icvs,
176  &team->t.t_implicit_task_taskdata[0].td_icvs);
177  }
178  ngo_sync();
179  }
180  }
181 #endif // KMP_BARRIER_ICV_PUSH
182 
183  // Now, release all of the worker threads
184  for (i = 1; i < nproc; ++i) {
185 #if KMP_CACHE_MANAGE
186  // Prefetch next thread's go flag
187  if (i + 1 < nproc)
188  KMP_CACHE_PREFETCH(&other_threads[i + 1]->th.th_bar[bt].bb.b_go);
189 #endif /* KMP_CACHE_MANAGE */
190  KA_TRACE(
191  20,
192  ("__kmp_linear_barrier_release: T#%d(%d:%d) releasing T#%d(%d:%d) "
193  "go(%p): %u => %u\n",
194  gtid, team->t.t_id, tid, other_threads[i]->th.th_info.ds.ds_gtid,
195  team->t.t_id, i, &other_threads[i]->th.th_bar[bt].bb.b_go,
196  other_threads[i]->th.th_bar[bt].bb.b_go,
197  other_threads[i]->th.th_bar[bt].bb.b_go + KMP_BARRIER_STATE_BUMP));
198  kmp_flag_64<> flag(&other_threads[i]->th.th_bar[bt].bb.b_go,
199  other_threads[i]);
200  flag.release();
201  }
202  }
203  } else { // Wait for the PRIMARY thread to release us
204  KA_TRACE(20, ("__kmp_linear_barrier_release: T#%d wait go(%p) == %u\n",
205  gtid, &thr_bar->b_go, KMP_BARRIER_STATE_BUMP));
206  if (cancellable) {
207  kmp_flag_64<true, false> flag(&thr_bar->b_go, KMP_BARRIER_STATE_BUMP);
208  if (flag.wait(this_thr, TRUE USE_ITT_BUILD_ARG(itt_sync_obj)))
209  return true;
210  } else {
211  kmp_flag_64<> flag(&thr_bar->b_go, KMP_BARRIER_STATE_BUMP);
212  flag.wait(this_thr, TRUE USE_ITT_BUILD_ARG(itt_sync_obj));
213  }
214 #if USE_ITT_BUILD && USE_ITT_NOTIFY
215  if ((__itt_sync_create_ptr && itt_sync_obj == NULL) || KMP_ITT_DEBUG) {
216  // In a fork barrier; cannot get the object reliably (or ITTNOTIFY is
217  // disabled)
218  itt_sync_obj = __kmp_itt_barrier_object(gtid, bs_forkjoin_barrier, 0, -1);
219  // Cancel wait on previous parallel region...
220  __kmp_itt_task_starting(itt_sync_obj);
221 
222  if (bt == bs_forkjoin_barrier && TCR_4(__kmp_global.g.g_done))
223  return false;
224 
225  itt_sync_obj = __kmp_itt_barrier_object(gtid, bs_forkjoin_barrier);
226  if (itt_sync_obj != NULL)
227  // Call prepare as early as possible for "new" barrier
228  __kmp_itt_task_finished(itt_sync_obj);
229  } else
230 #endif /* USE_ITT_BUILD && USE_ITT_NOTIFY */
231  // Early exit for reaping threads releasing forkjoin barrier
232  if (bt == bs_forkjoin_barrier && TCR_4(__kmp_global.g.g_done))
233  return false;
234 // The worker thread may now assume that the team is valid.
235 #ifdef KMP_DEBUG
236  tid = __kmp_tid_from_gtid(gtid);
237  team = __kmp_threads[gtid]->th.th_team;
238 #endif
239  KMP_DEBUG_ASSERT(team != NULL);
240  TCW_4(thr_bar->b_go, KMP_INIT_BARRIER_STATE);
241  KA_TRACE(20,
242  ("__kmp_linear_barrier_release: T#%d(%d:%d) set go(%p) = %u\n",
243  gtid, team->t.t_id, tid, &thr_bar->b_go, KMP_INIT_BARRIER_STATE));
244  KMP_MB(); // Flush all pending memory write invalidates.
245  }
246  KA_TRACE(
247  20,
248  ("__kmp_linear_barrier_release: T#%d(%d:%d) exit for barrier type %d\n",
249  gtid, team->t.t_id, tid, bt));
250  return false;
251 }
252 
253 static void __kmp_linear_barrier_gather(
254  enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
255  void (*reduce)(void *, void *) USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
256  __kmp_linear_barrier_gather_template<false>(
257  bt, this_thr, gtid, tid, reduce USE_ITT_BUILD_ARG(itt_sync_obj));
258 }
259 
260 static bool __kmp_linear_barrier_gather_cancellable(
261  enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
262  void (*reduce)(void *, void *) USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
263  return __kmp_linear_barrier_gather_template<true>(
264  bt, this_thr, gtid, tid, reduce USE_ITT_BUILD_ARG(itt_sync_obj));
265 }
266 
267 static void __kmp_linear_barrier_release(
268  enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
269  int propagate_icvs USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
270  __kmp_linear_barrier_release_template<false>(
271  bt, this_thr, gtid, tid, propagate_icvs USE_ITT_BUILD_ARG(itt_sync_obj));
272 }
273 
274 static bool __kmp_linear_barrier_release_cancellable(
275  enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
276  int propagate_icvs USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
277  return __kmp_linear_barrier_release_template<true>(
278  bt, this_thr, gtid, tid, propagate_icvs USE_ITT_BUILD_ARG(itt_sync_obj));
279 }
280 
281 // Tree barrier
282 static void __kmp_tree_barrier_gather(
283  enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
284  void (*reduce)(void *, void *) USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
285  KMP_TIME_DEVELOPER_PARTITIONED_BLOCK(KMP_tree_gather);
286  kmp_team_t *team = this_thr->th.th_team;
287  kmp_bstate_t *thr_bar = &this_thr->th.th_bar[bt].bb;
288  kmp_info_t **other_threads = team->t.t_threads;
289  kmp_uint32 nproc = this_thr->th.th_team_nproc;
290  kmp_uint32 branch_bits = __kmp_barrier_gather_branch_bits[bt];
291  kmp_uint32 branch_factor = 1 << branch_bits;
292  kmp_uint32 child;
293  kmp_uint32 child_tid;
294  kmp_uint64 new_state = 0;
295 
296  KA_TRACE(
297  20, ("__kmp_tree_barrier_gather: T#%d(%d:%d) enter for barrier type %d\n",
298  gtid, team->t.t_id, tid, bt));
299  KMP_DEBUG_ASSERT(this_thr == other_threads[this_thr->th.th_info.ds.ds_tid]);
300 
301 #if USE_ITT_BUILD && USE_ITT_NOTIFY
302  // Barrier imbalance - save arrive time to the thread
303  if (__kmp_forkjoin_frames_mode == 3 || __kmp_forkjoin_frames_mode == 2) {
304  this_thr->th.th_bar_arrive_time = this_thr->th.th_bar_min_time =
305  __itt_get_timestamp();
306  }
307 #endif
308  // Perform tree gather to wait until all threads have arrived; reduce any
309  // required data as we go
310  child_tid = (tid << branch_bits) + 1;
311  if (child_tid < nproc) {
312  // Parent threads wait for all their children to arrive
313  new_state = team->t.t_bar[bt].b_arrived + KMP_BARRIER_STATE_BUMP;
314  child = 1;
315  do {
316  kmp_info_t *child_thr = other_threads[child_tid];
317  kmp_bstate_t *child_bar = &child_thr->th.th_bar[bt].bb;
318 #if KMP_CACHE_MANAGE
319  // Prefetch next thread's arrived count
320  if (child + 1 <= branch_factor && child_tid + 1 < nproc)
321  KMP_CACHE_PREFETCH(
322  &other_threads[child_tid + 1]->th.th_bar[bt].bb.b_arrived);
323 #endif /* KMP_CACHE_MANAGE */
324  KA_TRACE(20,
325  ("__kmp_tree_barrier_gather: T#%d(%d:%d) wait T#%d(%d:%u) "
326  "arrived(%p) == %llu\n",
327  gtid, team->t.t_id, tid, __kmp_gtid_from_tid(child_tid, team),
328  team->t.t_id, child_tid, &child_bar->b_arrived, new_state));
329  // Wait for child to arrive
330  kmp_flag_64<> flag(&child_bar->b_arrived, new_state);
331  flag.wait(this_thr, FALSE USE_ITT_BUILD_ARG(itt_sync_obj));
332 #if USE_ITT_BUILD && USE_ITT_NOTIFY
333  // Barrier imbalance - write min of the thread time and a child time to
334  // the thread.
335  if (__kmp_forkjoin_frames_mode == 2) {
336  this_thr->th.th_bar_min_time = KMP_MIN(this_thr->th.th_bar_min_time,
337  child_thr->th.th_bar_min_time);
338  }
339 #endif
340  if (reduce) {
341  KA_TRACE(100,
342  ("__kmp_tree_barrier_gather: T#%d(%d:%d) += T#%d(%d:%u)\n",
343  gtid, team->t.t_id, tid, __kmp_gtid_from_tid(child_tid, team),
344  team->t.t_id, child_tid));
345  OMPT_REDUCTION_DECL(this_thr, gtid);
346  OMPT_REDUCTION_BEGIN;
347  (*reduce)(this_thr->th.th_local.reduce_data,
348  child_thr->th.th_local.reduce_data);
349  OMPT_REDUCTION_END;
350  }
351  child++;
352  child_tid++;
353  } while (child <= branch_factor && child_tid < nproc);
354  }
355 
356  if (!KMP_MASTER_TID(tid)) { // Worker threads
357  kmp_int32 parent_tid = (tid - 1) >> branch_bits;
358 
359  KA_TRACE(20,
360  ("__kmp_tree_barrier_gather: T#%d(%d:%d) releasing T#%d(%d:%d) "
361  "arrived(%p): %llu => %llu\n",
362  gtid, team->t.t_id, tid, __kmp_gtid_from_tid(parent_tid, team),
363  team->t.t_id, parent_tid, &thr_bar->b_arrived, thr_bar->b_arrived,
364  thr_bar->b_arrived + KMP_BARRIER_STATE_BUMP));
365 
366  // Mark arrival to parent thread
367  /* After performing this write, a worker thread may not assume that the team
368  is valid any more - it could be deallocated by the primary thread at any
369  time. */
370  kmp_flag_64<> flag(&thr_bar->b_arrived, other_threads[parent_tid]);
371  flag.release();
372  } else {
373  // Need to update the team arrived pointer if we are the primary thread
374  if (nproc > 1) // New value was already computed above
375  team->t.t_bar[bt].b_arrived = new_state;
376  else
377  team->t.t_bar[bt].b_arrived += KMP_BARRIER_STATE_BUMP;
378  KA_TRACE(20, ("__kmp_tree_barrier_gather: T#%d(%d:%d) set team %d "
379  "arrived(%p) = %llu\n",
380  gtid, team->t.t_id, tid, team->t.t_id,
381  &team->t.t_bar[bt].b_arrived, team->t.t_bar[bt].b_arrived));
382  }
383  KA_TRACE(20,
384  ("__kmp_tree_barrier_gather: T#%d(%d:%d) exit for barrier type %d\n",
385  gtid, team->t.t_id, tid, bt));
386 }
387 
388 static void __kmp_tree_barrier_release(
389  enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
390  int propagate_icvs USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
391  KMP_TIME_DEVELOPER_PARTITIONED_BLOCK(KMP_tree_release);
392  kmp_team_t *team;
393  kmp_bstate_t *thr_bar = &this_thr->th.th_bar[bt].bb;
394  kmp_uint32 nproc;
395  kmp_uint32 branch_bits = __kmp_barrier_release_branch_bits[bt];
396  kmp_uint32 branch_factor = 1 << branch_bits;
397  kmp_uint32 child;
398  kmp_uint32 child_tid;
399 
400  // Perform a tree release for all of the threads that have been gathered
401  if (!KMP_MASTER_TID(
402  tid)) { // Handle fork barrier workers who aren't part of a team yet
403  KA_TRACE(20, ("__kmp_tree_barrier_release: T#%d wait go(%p) == %u\n", gtid,
404  &thr_bar->b_go, KMP_BARRIER_STATE_BUMP));
405  // Wait for parent thread to release us
406  kmp_flag_64<> flag(&thr_bar->b_go, KMP_BARRIER_STATE_BUMP);
407  flag.wait(this_thr, TRUE USE_ITT_BUILD_ARG(itt_sync_obj));
408 #if USE_ITT_BUILD && USE_ITT_NOTIFY
409  if ((__itt_sync_create_ptr && itt_sync_obj == NULL) || KMP_ITT_DEBUG) {
410  // In fork barrier where we could not get the object reliably (or
411  // ITTNOTIFY is disabled)
412  itt_sync_obj = __kmp_itt_barrier_object(gtid, bs_forkjoin_barrier, 0, -1);
413  // Cancel wait on previous parallel region...
414  __kmp_itt_task_starting(itt_sync_obj);
415 
416  if (bt == bs_forkjoin_barrier && TCR_4(__kmp_global.g.g_done))
417  return;
418 
419  itt_sync_obj = __kmp_itt_barrier_object(gtid, bs_forkjoin_barrier);
420  if (itt_sync_obj != NULL)
421  // Call prepare as early as possible for "new" barrier
422  __kmp_itt_task_finished(itt_sync_obj);
423  } else
424 #endif /* USE_ITT_BUILD && USE_ITT_NOTIFY */
425  // Early exit for reaping threads releasing forkjoin barrier
426  if (bt == bs_forkjoin_barrier && TCR_4(__kmp_global.g.g_done))
427  return;
428