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