Update gcc-50 to SVN version 231263 (gcc-5-branch)
[dragonfly.git] / contrib / gcc-5.0 / libgomp / iter.c
1 /* Copyright (C) 2005-2015 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 contains routines for managing work-share iteration, both
27    for loops and sections.  */
28
29 #include "libgomp.h"
30 #include <stdlib.h>
31
32
33 /* This function implements the STATIC scheduling method.  The caller should
34    iterate *pstart <= x < *pend.  Return zero if there are more iterations
35    to perform; nonzero if not.  Return less than 0 if this thread had
36    received the absolutely last iteration.  */
37
38 int
39 gomp_iter_static_next (long *pstart, long *pend)
40 {
41   struct gomp_thread *thr = gomp_thread ();
42   struct gomp_team *team = thr->ts.team;
43   struct gomp_work_share *ws = thr->ts.work_share;
44   unsigned long nthreads = team ? team->nthreads : 1;
45
46   if (thr->ts.static_trip == -1)
47     return -1;
48
49   /* Quick test for degenerate teams and orphaned constructs.  */
50   if (nthreads == 1)
51     {
52       *pstart = ws->next;
53       *pend = ws->end;
54       thr->ts.static_trip = -1;
55       return ws->next == ws->end;
56     }
57
58   /* We interpret chunk_size zero as "unspecified", which means that we
59      should break up the iterations such that each thread makes only one
60      trip through the outer loop.  */
61   if (ws->chunk_size == 0)
62     {
63       unsigned long n, q, i, t;
64       unsigned long s0, e0;
65       long s, e;
66
67       if (thr->ts.static_trip > 0)
68         return 1;
69
70       /* Compute the total number of iterations.  */
71       s = ws->incr + (ws->incr > 0 ? -1 : 1);
72       n = (ws->end - ws->next + s) / ws->incr;
73       i = thr->ts.team_id;
74
75       /* Compute the "zero-based" start and end points.  That is, as
76          if the loop began at zero and incremented by one.  */
77       q = n / nthreads;
78       t = n % nthreads;
79       if (i < t)
80         {
81           t = 0;
82           q++;
83         }
84       s0 = q * i + t;
85       e0 = s0 + q;
86
87       /* Notice when no iterations allocated for this thread.  */
88       if (s0 >= e0)
89         {
90           thr->ts.static_trip = 1;
91           return 1;
92         }
93
94       /* Transform these to the actual start and end numbers.  */
95       s = (long)s0 * ws->incr + ws->next;
96       e = (long)e0 * ws->incr + ws->next;
97
98       *pstart = s;
99       *pend = e;
100       thr->ts.static_trip = (e0 == n ? -1 : 1);
101       return 0;
102     }
103   else
104     {
105       unsigned long n, s0, e0, i, c;
106       long s, e;
107
108       /* Otherwise, each thread gets exactly chunk_size iterations
109          (if available) each time through the loop.  */
110
111       s = ws->incr + (ws->incr > 0 ? -1 : 1);
112       n = (ws->end - ws->next + s) / ws->incr;
113       i = thr->ts.team_id;
114       c = ws->chunk_size;
115
116       /* Initial guess is a C sized chunk positioned nthreads iterations
117          in, offset by our thread number.  */
118       s0 = (thr->ts.static_trip * nthreads + i) * c;
119       e0 = s0 + c;
120
121       /* Detect overflow.  */
122       if (s0 >= n)
123         return 1;
124       if (e0 > n)
125         e0 = n;
126
127       /* Transform these to the actual start and end numbers.  */
128       s = (long)s0 * ws->incr + ws->next;
129       e = (long)e0 * ws->incr + ws->next;
130
131       *pstart = s;
132       *pend = e;
133
134       if (e0 == n)
135         thr->ts.static_trip = -1;
136       else
137         thr->ts.static_trip++;
138       return 0;
139     }
140 }
141
142
143 /* This function implements the DYNAMIC scheduling method.  Arguments are
144    as for gomp_iter_static_next.  This function must be called with ws->lock
145    held.  */
146
147 bool
148 gomp_iter_dynamic_next_locked (long *pstart, long *pend)
149 {
150   struct gomp_thread *thr = gomp_thread ();
151   struct gomp_work_share *ws = thr->ts.work_share;
152   long start, end, chunk, left;
153
154   start = ws->next;
155   if (start == ws->end)
156     return false;
157
158   chunk = ws->chunk_size;
159   left = ws->end - start;
160   if (ws->incr < 0)
161     {
162       if (chunk < left)
163         chunk = left;
164     }
165   else
166     {
167       if (chunk > left)
168         chunk = left;
169     }
170   end = start + chunk;
171
172   ws->next = end;
173   *pstart = start;
174   *pend = end;
175   return true;
176 }
177
178
179 #ifdef HAVE_SYNC_BUILTINS
180 /* Similar, but doesn't require the lock held, and uses compare-and-swap
181    instead.  Note that the only memory value that changes is ws->next.  */
182
183 bool
184 gomp_iter_dynamic_next (long *pstart, long *pend)
185 {
186   struct gomp_thread *thr = gomp_thread ();
187   struct gomp_work_share *ws = thr->ts.work_share;
188   long start, end, nend, chunk, incr;
189
190   end = ws->end;
191   incr = ws->incr;
192   chunk = ws->chunk_size;
193
194   if (__builtin_expect (ws->mode, 1))
195     {
196       long tmp = __sync_fetch_and_add (&ws->next, chunk);
197       if (incr > 0)
198         {
199           if (tmp >= end)
200             return false;
201           nend = tmp + chunk;
202           if (nend > end)
203             nend = end;
204           *pstart = tmp;
205           *pend = nend;
206           return true;
207         }
208       else
209         {
210           if (tmp <= end)
211             return false;
212           nend = tmp + chunk;
213           if (nend < end)
214             nend = end;
215           *pstart = tmp;
216           *pend = nend;
217           return true;
218         }
219     }
220
221   start = __atomic_load_n (&ws->next, MEMMODEL_RELAXED);
222   while (1)
223     {
224       long left = end - start;
225       long tmp;
226
227       if (start == end)
228         return false;
229
230       if (incr < 0)
231         {
232           if (chunk < left)
233             chunk = left;
234         }
235       else
236         {
237           if (chunk > left)
238             chunk = left;
239         }
240       nend = start + chunk;
241
242       tmp = __sync_val_compare_and_swap (&ws->next, start, nend);
243       if (__builtin_expect (tmp == start, 1))
244         break;
245
246       start = tmp;
247     }
248
249   *pstart = start;
250   *pend = nend;
251   return true;
252 }
253 #endif /* HAVE_SYNC_BUILTINS */
254
255
256 /* This function implements the GUIDED scheduling method.  Arguments are
257    as for gomp_iter_static_next.  This function must be called with the
258    work share lock held.  */
259
260 bool
261 gomp_iter_guided_next_locked (long *pstart, long *pend)
262 {
263   struct gomp_thread *thr = gomp_thread ();
264   struct gomp_work_share *ws = thr->ts.work_share;
265   struct gomp_team *team = thr->ts.team;
266   unsigned long nthreads = team ? team->nthreads : 1;
267   unsigned long n, q;
268   long start, end;
269
270   if (ws->next == ws->end)
271     return false;
272
273   start = ws->next;
274   n = (ws->end - start) / ws->incr;
275   q = (n + nthreads - 1) / nthreads;
276
277   if (q < ws->chunk_size)
278     q = ws->chunk_size;
279   if (q <= n)
280     end = start + q * ws->incr;
281   else
282     end = ws->end;
283
284   ws->next = end;
285   *pstart = start;
286   *pend = end;
287   return true;
288 }
289
290 #ifdef HAVE_SYNC_BUILTINS
291 /* Similar, but doesn't require the lock held, and uses compare-and-swap
292    instead.  Note that the only memory value that changes is ws->next.  */
293
294 bool
295 gomp_iter_guided_next (long *pstart, long *pend)
296 {
297   struct gomp_thread *thr = gomp_thread ();
298   struct gomp_work_share *ws = thr->ts.work_share;
299   struct gomp_team *team = thr->ts.team;
300   unsigned long nthreads = team ? team->nthreads : 1;
301   long start, end, nend, incr;
302   unsigned long chunk_size;
303
304   start = __atomic_load_n (&ws->next, MEMMODEL_RELAXED);
305   end = ws->end;
306   incr = ws->incr;
307   chunk_size = ws->chunk_size;
308
309   while (1)
310     {
311       unsigned long n, q;
312       long tmp;
313
314       if (start == end)
315         return false;
316
317       n = (end - start) / incr;
318       q = (n + nthreads - 1) / nthreads;
319
320       if (q < chunk_size)
321         q = chunk_size;
322       if (__builtin_expect (q <= n, 1))
323         nend = start + q * incr;
324       else
325         nend = end;
326
327       tmp = __sync_val_compare_and_swap (&ws->next, start, nend);
328       if (__builtin_expect (tmp == start, 1))
329         break;
330
331       start = tmp;
332     }
333
334   *pstart = start;
335   *pend = nend;
336   return true;
337 }
338 #endif /* HAVE_SYNC_BUILTINS */