sections.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /* Copyright (C) 2005-2022 Free Software Foundation, Inc.
  2. Contributed by Richard Henderson <rth@redhat.com>.
  3. This file is part of the GNU Offloading and Multi Processing Library
  4. (libgomp).
  5. Libgomp is free software; you can redistribute it and/or modify it
  6. under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 3, or (at your option)
  8. any later version.
  9. Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12. more details.
  13. Under Section 7 of GPL version 3, you are granted additional
  14. permissions described in the GCC Runtime Library Exception, version
  15. 3.1, as published by the Free Software Foundation.
  16. You should have received a copy of the GNU General Public License and
  17. a copy of the GCC Runtime Library Exception along with this program;
  18. see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
  19. <http://www.gnu.org/licenses/>. */
  20. /* This file handles the SECTIONS construct. */
  21. #include "libgomp.h"
  22. #include <string.h>
  23. ialias_redirect (GOMP_taskgroup_reduction_register)
  24. /* Initialize the given work share construct from the given arguments. */
  25. static inline void
  26. gomp_sections_init (struct gomp_work_share *ws, unsigned count)
  27. {
  28. ws->sched = GFS_DYNAMIC;
  29. ws->chunk_size = 1;
  30. ws->end = count + 1L;
  31. ws->incr = 1;
  32. ws->next = 1;
  33. #ifdef HAVE_SYNC_BUILTINS
  34. /* Prepare things to make each iteration faster. */
  35. if (sizeof (long) > sizeof (unsigned))
  36. ws->mode = 1;
  37. else
  38. {
  39. struct gomp_thread *thr = gomp_thread ();
  40. struct gomp_team *team = thr->ts.team;
  41. long nthreads = team ? team->nthreads : 1;
  42. ws->mode = ((nthreads | ws->end)
  43. < 1UL << (sizeof (long) * __CHAR_BIT__ / 2 - 1));
  44. }
  45. #else
  46. ws->mode = 0;
  47. #endif
  48. }
  49. /* This routine is called when first encountering a sections construct
  50. that is not bound directly to a parallel construct. The first thread
  51. that arrives will create the work-share construct; subsequent threads
  52. will see the construct exists and allocate work from it.
  53. COUNT is the number of sections in this construct.
  54. Returns the 1-based section number for this thread to perform, or 0 if
  55. all work was assigned to other threads prior to this thread's arrival. */
  56. unsigned
  57. GOMP_sections_start (unsigned count)
  58. {
  59. struct gomp_thread *thr = gomp_thread ();
  60. long s, e, ret;
  61. if (gomp_work_share_start (0))
  62. {
  63. gomp_sections_init (thr->ts.work_share, count);
  64. gomp_work_share_init_done ();
  65. }
  66. #ifdef HAVE_SYNC_BUILTINS
  67. if (gomp_iter_dynamic_next (&s, &e))
  68. ret = s;
  69. else
  70. ret = 0;
  71. #else
  72. gomp_mutex_lock (&thr->ts.work_share->lock);
  73. if (gomp_iter_dynamic_next_locked (&s, &e))
  74. ret = s;
  75. else
  76. ret = 0;
  77. gomp_mutex_unlock (&thr->ts.work_share->lock);
  78. #endif
  79. return ret;
  80. }
  81. unsigned
  82. GOMP_sections2_start (unsigned count, uintptr_t *reductions, void **mem)
  83. {
  84. struct gomp_thread *thr = gomp_thread ();
  85. long s, e, ret;
  86. if (reductions)
  87. gomp_workshare_taskgroup_start ();
  88. if (gomp_work_share_start (0))
  89. {
  90. gomp_sections_init (thr->ts.work_share, count);
  91. if (reductions)
  92. {
  93. GOMP_taskgroup_reduction_register (reductions);
  94. thr->task->taskgroup->workshare = true;
  95. thr->ts.work_share->task_reductions = reductions;
  96. }
  97. if (mem)
  98. {
  99. uintptr_t size = (uintptr_t) *mem;
  100. #define INLINE_ORDERED_TEAM_IDS_OFF \
  101. ((offsetof (struct gomp_work_share, inline_ordered_team_ids) \
  102. + __alignof__ (long long) - 1) & ~(__alignof__ (long long) - 1))
  103. if (size > (sizeof (struct gomp_work_share)
  104. - INLINE_ORDERED_TEAM_IDS_OFF))
  105. *mem
  106. = (void *) (thr->ts.work_share->ordered_team_ids
  107. = gomp_malloc_cleared (size));
  108. else
  109. *mem = memset (((char *) thr->ts.work_share)
  110. + INLINE_ORDERED_TEAM_IDS_OFF, '\0', size);
  111. }
  112. gomp_work_share_init_done ();
  113. }
  114. else
  115. {
  116. if (reductions)
  117. {
  118. uintptr_t *first_reductions = thr->ts.work_share->task_reductions;
  119. gomp_workshare_task_reduction_register (reductions,
  120. first_reductions);
  121. }
  122. if (mem)
  123. {
  124. if ((offsetof (struct gomp_work_share, inline_ordered_team_ids)
  125. & (__alignof__ (long long) - 1)) == 0)
  126. *mem = (void *) thr->ts.work_share->ordered_team_ids;
  127. else
  128. {
  129. uintptr_t p = (uintptr_t) thr->ts.work_share->ordered_team_ids;
  130. p += __alignof__ (long long) - 1;
  131. p &= ~(__alignof__ (long long) - 1);
  132. *mem = (void *) p;
  133. }
  134. }
  135. }
  136. #ifdef HAVE_SYNC_BUILTINS
  137. if (gomp_iter_dynamic_next (&s, &e))
  138. ret = s;
  139. else
  140. ret = 0;
  141. #else
  142. gomp_mutex_lock (&thr->ts.work_share->lock);
  143. if (gomp_iter_dynamic_next_locked (&s, &e))
  144. ret = s;
  145. else
  146. ret = 0;
  147. gomp_mutex_unlock (&thr->ts.work_share->lock);
  148. #endif
  149. return ret;
  150. }
  151. /* This routine is called when the thread completes processing of the
  152. section currently assigned to it. If the work-share construct is
  153. bound directly to a parallel construct, then the construct may have
  154. been set up before the parallel. In which case, this may be the
  155. first iteration for the thread.
  156. Returns the 1-based section number for this thread to perform, or 0 if
  157. all work was assigned to other threads prior to this thread's arrival. */
  158. unsigned
  159. GOMP_sections_next (void)
  160. {
  161. long s, e, ret;
  162. #ifdef HAVE_SYNC_BUILTINS
  163. if (gomp_iter_dynamic_next (&s, &e))
  164. ret = s;
  165. else
  166. ret = 0;
  167. #else
  168. struct gomp_thread *thr = gomp_thread ();
  169. gomp_mutex_lock (&thr->ts.work_share->lock);
  170. if (gomp_iter_dynamic_next_locked (&s, &e))
  171. ret = s;
  172. else
  173. ret = 0;
  174. gomp_mutex_unlock (&thr->ts.work_share->lock);
  175. #endif
  176. return ret;
  177. }
  178. /* This routine pre-initializes a work-share construct to avoid one
  179. synchronization once we get into the loop. */
  180. void
  181. GOMP_parallel_sections_start (void (*fn) (void *), void *data,
  182. unsigned num_threads, unsigned count)
  183. {
  184. struct gomp_team *team;
  185. num_threads = gomp_resolve_num_threads (num_threads, count);
  186. team = gomp_new_team (num_threads);
  187. gomp_sections_init (&team->work_shares[0], count);
  188. gomp_team_start (fn, data, num_threads, 0, team, NULL);
  189. }
  190. ialias_redirect (GOMP_parallel_end)
  191. void
  192. GOMP_parallel_sections (void (*fn) (void *), void *data,
  193. unsigned num_threads, unsigned count, unsigned flags)
  194. {
  195. struct gomp_team *team;
  196. num_threads = gomp_resolve_num_threads (num_threads, count);
  197. team = gomp_new_team (num_threads);
  198. gomp_sections_init (&team->work_shares[0], count);
  199. gomp_team_start (fn, data, num_threads, flags, team, NULL);
  200. fn (data);
  201. GOMP_parallel_end ();
  202. }
  203. /* The GOMP_section_end* routines are called after the thread is told
  204. that all sections are complete. The first two versions synchronize
  205. all threads; the nowait version does not. */
  206. void
  207. GOMP_sections_end (void)
  208. {
  209. gomp_work_share_end ();
  210. }
  211. bool
  212. GOMP_sections_end_cancel (void)
  213. {
  214. return gomp_work_share_end_cancel ();
  215. }
  216. void
  217. GOMP_sections_end_nowait (void)
  218. {
  219. gomp_work_share_end_nowait ();
  220. }