xref: /dflybsd-src/contrib/gcc-8.0/libgomp/priority_queue.c (revision 38fd149817dfbff97799f62fcb70be98c4e32523)
1*38fd1498Szrj /* Copyright (C) 2015-2018 Free Software Foundation, Inc.
2*38fd1498Szrj    Contributed by Aldy Hernandez <aldyh@redhat.com>.
3*38fd1498Szrj 
4*38fd1498Szrj    This file is part of the GNU Offloading and Multi Processing Library
5*38fd1498Szrj    (libgomp).
6*38fd1498Szrj 
7*38fd1498Szrj    Libgomp is free software; you can redistribute it and/or modify it
8*38fd1498Szrj    under the terms of the GNU General Public License as published by
9*38fd1498Szrj    the Free Software Foundation; either version 3, or (at your option)
10*38fd1498Szrj    any later version.
11*38fd1498Szrj 
12*38fd1498Szrj    Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
13*38fd1498Szrj    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14*38fd1498Szrj    FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15*38fd1498Szrj    more details.
16*38fd1498Szrj 
17*38fd1498Szrj    Under Section 7 of GPL version 3, you are granted additional
18*38fd1498Szrj    permissions described in the GCC Runtime Library Exception, version
19*38fd1498Szrj    3.1, as published by the Free Software Foundation.
20*38fd1498Szrj 
21*38fd1498Szrj    You should have received a copy of the GNU General Public License and
22*38fd1498Szrj    a copy of the GCC Runtime Library Exception along with this program;
23*38fd1498Szrj    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24*38fd1498Szrj    <http://www.gnu.org/licenses/>.  */
25*38fd1498Szrj 
26*38fd1498Szrj /* Priority queue implementation of GOMP tasks.  */
27*38fd1498Szrj 
28*38fd1498Szrj #include "libgomp.h"
29*38fd1498Szrj 
30*38fd1498Szrj #if _LIBGOMP_CHECKING_
31*38fd1498Szrj #include <stdio.h>
32*38fd1498Szrj 
33*38fd1498Szrj /* Sanity check to verify whether a TASK is in LIST.  Return TRUE if
34*38fd1498Szrj    found, FALSE otherwise.
35*38fd1498Szrj 
36*38fd1498Szrj    TYPE is the type of priority queue this task resides in.  */
37*38fd1498Szrj 
38*38fd1498Szrj static inline bool
priority_queue_task_in_list_p(enum priority_queue_type type,struct priority_list * list,struct gomp_task * task)39*38fd1498Szrj priority_queue_task_in_list_p (enum priority_queue_type type,
40*38fd1498Szrj 			       struct priority_list *list,
41*38fd1498Szrj 			       struct gomp_task *task)
42*38fd1498Szrj {
43*38fd1498Szrj   struct priority_node *p = list->tasks;
44*38fd1498Szrj   do
45*38fd1498Szrj     {
46*38fd1498Szrj       if (priority_node_to_task (type, p) == task)
47*38fd1498Szrj 	return true;
48*38fd1498Szrj       p = p->next;
49*38fd1498Szrj     }
50*38fd1498Szrj   while (p != list->tasks);
51*38fd1498Szrj   return false;
52*38fd1498Szrj }
53*38fd1498Szrj 
54*38fd1498Szrj /* Tree version of priority_queue_task_in_list_p.  */
55*38fd1498Szrj 
56*38fd1498Szrj static inline bool
priority_queue_task_in_tree_p(enum priority_queue_type type,struct priority_queue * head,struct gomp_task * task)57*38fd1498Szrj priority_queue_task_in_tree_p (enum priority_queue_type type,
58*38fd1498Szrj 			       struct priority_queue *head,
59*38fd1498Szrj 			       struct gomp_task *task)
60*38fd1498Szrj {
61*38fd1498Szrj   struct priority_list *list
62*38fd1498Szrj     = priority_queue_lookup_priority (head, task->priority);
63*38fd1498Szrj   if (!list)
64*38fd1498Szrj     return false;
65*38fd1498Szrj   return priority_queue_task_in_list_p (type, list, task);
66*38fd1498Szrj }
67*38fd1498Szrj 
68*38fd1498Szrj /* Generic version of priority_queue_task_in_list_p that works for
69*38fd1498Szrj    trees or lists.  */
70*38fd1498Szrj 
71*38fd1498Szrj bool
priority_queue_task_in_queue_p(enum priority_queue_type type,struct priority_queue * head,struct gomp_task * task)72*38fd1498Szrj priority_queue_task_in_queue_p (enum priority_queue_type type,
73*38fd1498Szrj 				struct priority_queue *head,
74*38fd1498Szrj 				struct gomp_task *task)
75*38fd1498Szrj {
76*38fd1498Szrj   if (priority_queue_empty_p (head, MEMMODEL_RELAXED))
77*38fd1498Szrj     return false;
78*38fd1498Szrj   if (priority_queue_multi_p (head))
79*38fd1498Szrj     return priority_queue_task_in_tree_p (type, head, task);
80*38fd1498Szrj   else
81*38fd1498Szrj     return priority_queue_task_in_list_p (type, &head->l, task);
82*38fd1498Szrj }
83*38fd1498Szrj 
84*38fd1498Szrj /* Sanity check LIST to make sure the tasks therein are in the right
85*38fd1498Szrj    order.  LIST is a priority list of type TYPE.
86*38fd1498Szrj 
87*38fd1498Szrj    The expected order is that GOMP_TASK_WAITING tasks come before
88*38fd1498Szrj    GOMP_TASK_TIED/GOMP_TASK_ASYNC_RUNNING ones.
89*38fd1498Szrj 
90*38fd1498Szrj    If CHECK_DEPS is TRUE, we also check that parent_depends_on WAITING
91*38fd1498Szrj    tasks come before !parent_depends_on WAITING tasks.  This is only
92*38fd1498Szrj    applicable to the children queue, and the caller is expected to
93*38fd1498Szrj    ensure that we are verifying the children queue.  */
94*38fd1498Szrj 
95*38fd1498Szrj static void
priority_list_verify(enum priority_queue_type type,struct priority_list * list,bool check_deps)96*38fd1498Szrj priority_list_verify (enum priority_queue_type type,
97*38fd1498Szrj 		      struct priority_list *list, bool check_deps)
98*38fd1498Szrj {
99*38fd1498Szrj   bool seen_tied = false;
100*38fd1498Szrj   bool seen_plain_waiting = false;
101*38fd1498Szrj   struct priority_node *p = list->tasks;
102*38fd1498Szrj   while (1)
103*38fd1498Szrj     {
104*38fd1498Szrj       struct gomp_task *t = priority_node_to_task (type, p);
105*38fd1498Szrj       if (seen_tied && t->kind == GOMP_TASK_WAITING)
106*38fd1498Szrj 	gomp_fatal ("priority_queue_verify: WAITING task after TIED");
107*38fd1498Szrj       if (t->kind >= GOMP_TASK_TIED)
108*38fd1498Szrj 	seen_tied = true;
109*38fd1498Szrj       else if (check_deps && t->kind == GOMP_TASK_WAITING)
110*38fd1498Szrj 	{
111*38fd1498Szrj 	  if (t->parent_depends_on)
112*38fd1498Szrj 	    {
113*38fd1498Szrj 	      if (seen_plain_waiting)
114*38fd1498Szrj 		gomp_fatal ("priority_queue_verify: "
115*38fd1498Szrj 			    "parent_depends_on after !parent_depends_on");
116*38fd1498Szrj 	    }
117*38fd1498Szrj 	  else
118*38fd1498Szrj 	    seen_plain_waiting = true;
119*38fd1498Szrj 	}
120*38fd1498Szrj       p = p->next;
121*38fd1498Szrj       if (p == list->tasks)
122*38fd1498Szrj 	break;
123*38fd1498Szrj     }
124*38fd1498Szrj }
125*38fd1498Szrj 
126*38fd1498Szrj /* Callback type for priority_tree_verify_callback.  */
127*38fd1498Szrj struct cbtype
128*38fd1498Szrj {
129*38fd1498Szrj   enum priority_queue_type type;
130*38fd1498Szrj   bool check_deps;
131*38fd1498Szrj };
132*38fd1498Szrj 
133*38fd1498Szrj /* Verify every task in NODE.
134*38fd1498Szrj 
135*38fd1498Szrj    Callback for splay_tree_foreach.  */
136*38fd1498Szrj 
137*38fd1498Szrj static void
priority_tree_verify_callback(prio_splay_tree_key key,void * data)138*38fd1498Szrj priority_tree_verify_callback (prio_splay_tree_key key, void *data)
139*38fd1498Szrj {
140*38fd1498Szrj   struct cbtype *cb = (struct cbtype *) data;
141*38fd1498Szrj   priority_list_verify (cb->type, &key->l, cb->check_deps);
142*38fd1498Szrj }
143*38fd1498Szrj 
144*38fd1498Szrj /* Generic version of priority_list_verify.
145*38fd1498Szrj 
146*38fd1498Szrj    Sanity check HEAD to make sure the tasks therein are in the right
147*38fd1498Szrj    order.  The priority_queue holds tasks of type TYPE.
148*38fd1498Szrj 
149*38fd1498Szrj    If CHECK_DEPS is TRUE, we also check that parent_depends_on WAITING
150*38fd1498Szrj    tasks come before !parent_depends_on WAITING tasks.  This is only
151*38fd1498Szrj    applicable to the children queue, and the caller is expected to
152*38fd1498Szrj    ensure that we are verifying the children queue.  */
153*38fd1498Szrj 
154*38fd1498Szrj void
priority_queue_verify(enum priority_queue_type type,struct priority_queue * head,bool check_deps)155*38fd1498Szrj priority_queue_verify (enum priority_queue_type type,
156*38fd1498Szrj 		       struct priority_queue *head, bool check_deps)
157*38fd1498Szrj {
158*38fd1498Szrj   if (priority_queue_empty_p (head, MEMMODEL_RELAXED))
159*38fd1498Szrj     return;
160*38fd1498Szrj   if (priority_queue_multi_p (head))
161*38fd1498Szrj     {
162*38fd1498Szrj       struct cbtype cb = { type, check_deps };
163*38fd1498Szrj       prio_splay_tree_foreach (&head->t,
164*38fd1498Szrj 			       priority_tree_verify_callback, &cb);
165*38fd1498Szrj     }
166*38fd1498Szrj   else
167*38fd1498Szrj     priority_list_verify (type, &head->l, check_deps);
168*38fd1498Szrj }
169*38fd1498Szrj #endif /* _LIBGOMP_CHECKING_ */
170*38fd1498Szrj 
171*38fd1498Szrj /* Remove NODE from priority queue HEAD, wherever it may be inside the
172*38fd1498Szrj    tree.  HEAD contains tasks of type TYPE.  */
173*38fd1498Szrj 
174*38fd1498Szrj void
priority_tree_remove(enum priority_queue_type type,struct priority_queue * head,struct priority_node * node)175*38fd1498Szrj priority_tree_remove (enum priority_queue_type type,
176*38fd1498Szrj 		      struct priority_queue *head,
177*38fd1498Szrj 		      struct priority_node *node)
178*38fd1498Szrj {
179*38fd1498Szrj   /* ?? The only reason this function is not inlined is because we
180*38fd1498Szrj      need to find the priority within gomp_task (which has not been
181*38fd1498Szrj      completely defined in the header file).  If the lack of inlining
182*38fd1498Szrj      is a concern, we could pass the priority number as a
183*38fd1498Szrj      parameter, or we could move this to libgomp.h.  */
184*38fd1498Szrj   int priority = priority_node_to_task (type, node)->priority;
185*38fd1498Szrj 
186*38fd1498Szrj   /* ?? We could avoid this lookup by keeping a pointer to the key in
187*38fd1498Szrj      the priority_node.  */
188*38fd1498Szrj   struct priority_list *list
189*38fd1498Szrj     = priority_queue_lookup_priority (head, priority);
190*38fd1498Szrj #if _LIBGOMP_CHECKING_
191*38fd1498Szrj   if (!list)
192*38fd1498Szrj     gomp_fatal ("Unable to find priority %d", priority);
193*38fd1498Szrj #endif
194*38fd1498Szrj   /* If NODE was the last in its priority, clean up the priority.  */
195*38fd1498Szrj   if (priority_list_remove (list, node, MEMMODEL_RELAXED))
196*38fd1498Szrj     {
197*38fd1498Szrj       prio_splay_tree_remove (&head->t, (prio_splay_tree_key) list);
198*38fd1498Szrj       list->tasks = NULL;
199*38fd1498Szrj #if _LIBGOMP_CHECKING_
200*38fd1498Szrj       memset (list, 0xaf, sizeof (*list));
201*38fd1498Szrj #endif
202*38fd1498Szrj       free (list);
203*38fd1498Szrj     }
204*38fd1498Szrj }
205*38fd1498Szrj 
206*38fd1498Szrj /* Return the highest priority WAITING task in a splay tree NODE.  If
207*38fd1498Szrj    there are no WAITING tasks available, return NULL.
208*38fd1498Szrj 
209*38fd1498Szrj    NODE is a priority list containing tasks of type TYPE.
210*38fd1498Szrj 
211*38fd1498Szrj    The right most node in a tree contains the highest priority.
212*38fd1498Szrj    Recurse down to find such a node.  If the task at that max node is
213*38fd1498Szrj    not WAITING, bubble back up and look at the remaining tasks
214*38fd1498Szrj    in-order.  */
215*38fd1498Szrj 
216*38fd1498Szrj static struct gomp_task *
priority_tree_next_task_1(enum priority_queue_type type,prio_splay_tree_node node)217*38fd1498Szrj priority_tree_next_task_1 (enum priority_queue_type type,
218*38fd1498Szrj 			   prio_splay_tree_node node)
219*38fd1498Szrj {
220*38fd1498Szrj  again:
221*38fd1498Szrj   if (!node)
222*38fd1498Szrj     return NULL;
223*38fd1498Szrj   struct gomp_task *ret = priority_tree_next_task_1 (type, node->right);
224*38fd1498Szrj   if (ret)
225*38fd1498Szrj     return ret;
226*38fd1498Szrj   ret = priority_node_to_task (type, node->key.l.tasks);
227*38fd1498Szrj   if (ret->kind == GOMP_TASK_WAITING)
228*38fd1498Szrj     return ret;
229*38fd1498Szrj   node = node->left;
230*38fd1498Szrj   goto again;
231*38fd1498Szrj }
232*38fd1498Szrj 
233*38fd1498Szrj /* Return the highest priority WAITING task from within Q1 and Q2,
234*38fd1498Szrj    while giving preference to tasks from Q1.  Q1 is a queue containing
235*38fd1498Szrj    items of type TYPE1.  Q2 is a queue containing items of type TYPE2.
236*38fd1498Szrj 
237*38fd1498Szrj    Since we are mostly interested in Q1, if there are no WAITING tasks
238*38fd1498Szrj    in Q1, we don't bother checking Q2, and just return NULL.
239*38fd1498Szrj 
240*38fd1498Szrj    As a special case, Q2 can be NULL, in which case, we just choose
241*38fd1498Szrj    the highest priority WAITING task in Q1.  This is an optimization
242*38fd1498Szrj    to speed up looking through only one queue.
243*38fd1498Szrj 
244*38fd1498Szrj    If the returned task is chosen from Q1, *Q1_CHOSEN_P is set to
245*38fd1498Szrj    TRUE, otherwise it is set to FALSE.  */
246*38fd1498Szrj 
247*38fd1498Szrj struct gomp_task *
priority_tree_next_task(enum priority_queue_type type1,struct priority_queue * q1,enum priority_queue_type type2,struct priority_queue * q2,bool * q1_chosen_p)248*38fd1498Szrj priority_tree_next_task (enum priority_queue_type type1,
249*38fd1498Szrj 			 struct priority_queue *q1,
250*38fd1498Szrj 			 enum priority_queue_type type2,
251*38fd1498Szrj 			 struct priority_queue *q2,
252*38fd1498Szrj 			 bool *q1_chosen_p)
253*38fd1498Szrj {
254*38fd1498Szrj   struct gomp_task *t1 = priority_tree_next_task_1 (type1, q1->t.root);
255*38fd1498Szrj   if (!t1
256*38fd1498Szrj       /* Special optimization when only searching through one queue.  */
257*38fd1498Szrj       || !q2)
258*38fd1498Szrj     {
259*38fd1498Szrj       *q1_chosen_p = true;
260*38fd1498Szrj       return t1;
261*38fd1498Szrj     }
262*38fd1498Szrj   struct gomp_task *t2 = priority_tree_next_task_1 (type2, q2->t.root);
263*38fd1498Szrj   if (!t2 || t1->priority > t2->priority)
264*38fd1498Szrj     {
265*38fd1498Szrj       *q1_chosen_p = true;
266*38fd1498Szrj       return t1;
267*38fd1498Szrj     }
268*38fd1498Szrj   if (t2->priority > t1->priority)
269*38fd1498Szrj     {
270*38fd1498Szrj       *q1_chosen_p = false;
271*38fd1498Szrj       return t2;
272*38fd1498Szrj     }
273*38fd1498Szrj   /* If we get here, the priorities are the same, so we must look at
274*38fd1498Szrj      parent_depends_on to make our decision.  */
275*38fd1498Szrj #if _LIBGOMP_CHECKING_
276*38fd1498Szrj   if (t1 != t2)
277*38fd1498Szrj     gomp_fatal ("priority_tree_next_task: t1 != t2");
278*38fd1498Szrj #endif
279*38fd1498Szrj   if (t2->parent_depends_on && !t1->parent_depends_on)
280*38fd1498Szrj     {
281*38fd1498Szrj       *q1_chosen_p = false;
282*38fd1498Szrj       return t2;
283*38fd1498Szrj     }
284*38fd1498Szrj   *q1_chosen_p = true;
285*38fd1498Szrj   return t1;
286*38fd1498Szrj }
287*38fd1498Szrj 
288*38fd1498Szrj /* Priority splay trees comparison function.  */
289*38fd1498Szrj static inline int
prio_splay_compare(prio_splay_tree_key x,prio_splay_tree_key y)290*38fd1498Szrj prio_splay_compare (prio_splay_tree_key x, prio_splay_tree_key y)
291*38fd1498Szrj {
292*38fd1498Szrj   if (x->l.priority == y->l.priority)
293*38fd1498Szrj     return 0;
294*38fd1498Szrj   return x->l.priority < y->l.priority ? -1 : 1;
295*38fd1498Szrj }
296*38fd1498Szrj 
297*38fd1498Szrj /* Define another splay tree instantiation, for priority_list's.  */
298*38fd1498Szrj #define splay_tree_prefix prio
299*38fd1498Szrj #define splay_tree_c
300*38fd1498Szrj #include "splay-tree.h"
301