gcc/libgomp/priority_queue.c

301 lines
9.1 KiB
C
Raw Normal View History

/* Copyright (C) 2015-2020 Free Software Foundation, Inc.
omp-low.c (lower_omp_ordered): Add argument to GOMP_SMD_ORDERED_* internal calls - 0 if... gcc/ 2015-11-14 Jakub Jelinek <jakub@redhat.com> * omp-low.c (lower_omp_ordered): Add argument to GOMP_SMD_ORDERED_* internal calls - 0 if ordered simd and 1 for ordered threads simd. * tree-vectorizer.c (adjust_simduid_builtins): If GOMP_SIMD_ORDERED_* argument is 1, replace it with GOMP_ordered_* call instead of removing it. gcc/c/ 2015-11-14 Jakub Jelinek <jakub@redhat.com> * c-typeck.c (c_finish_omp_clauses): Don't mark GOMP_MAP_FIRSTPRIVATE_POINTER decls addressable. gcc/cp/ 2015-11-14 Jakub Jelinek <jakub@redhat.com> * semantics.c (finish_omp_clauses): Don't mark GOMP_MAP_FIRSTPRIVATE_POINTER decls addressable. libgomp/ 2015-11-14 Jakub Jelinek <jakub@redhat.com> Aldy Hernandez <aldyh@redhat.com> Ilya Verbin <ilya.verbin@intel.com> * ordered.c (gomp_doacross_init, GOMP_doacross_post, GOMP_doacross_wait, gomp_doacross_ull_init, GOMP_doacross_ull_post, GOMP_doacross_ull_wait): For GFS_GUIDED don't divide number of iterators or IV by chunk size. * parallel.c (gomp_resolve_num_threads): Don't assume that if thr->ts.team is non-NULL, then pool must be non-NULL. * libgomp-plugin.h (GOMP_PLUGIN_target_task_completion): Declare. * libgomp.map (GOMP_PLUGIN_1.1): New symbol version, export GOMP_PLUGIN_target_task_completion. * Makefile.am (libgomp_la_SOURCES): Add priority_queue.c. * Makefile.in: Regenerate. * libgomp.h: Shuffle prototypes and forward definitions around so priority queues can be defined. (enum gomp_task_kind): Add GOMP_TASK_ASYNC_RUNNING. (enum gomp_target_task_state): New enum. (struct gomp_target_task): Add state, tgt, task and team fields. (gomp_create_target_task): Change return type to bool, add state argument. (gomp_target_task_fn): Change return type to bool. (struct gomp_device_descr): Add async_run_func. (struct gomp_task): Remove children, next_child, prev_child, next_queue, prev_queue, next_taskgroup, prev_taskgroup. Add pnode field. (struct gomp_taskgroup): Remove children. Add taskgroup_queue. (struct gomp_team): Change task_queue type to a priority queue. (splay_compare): Define inline. (priority_queue_offset): New. (priority_node_to_task): New. (task_to_priority_node): New. * oacc-mem.c: Do not include splay-tree.h. * priority_queue.c: New file. * priority_queue.h: New file. * splay-tree.c: Do not include splay-tree.h. (splay_tree_foreach_internal): New. (splay_tree_foreach): New. * splay-tree.h: Become re-entrant if splay_tree_prefix is defined. (splay_tree_callback): Define typedef. * target.c (splay_compare): Move to libgomp.h. (GOMP_target): Don't adjust *thr in any way around running offloaded task. (GOMP_target_ext): Likewise. Handle target nowait. (GOMP_target_update_ext, GOMP_target_enter_exit_data): Check return value from gomp_create_target_task, if false, fallthrough as if no dependencies exist. (gomp_target_task_fn): Change return type to bool, return true if the task should have another part scheduled later. Handle target nowait. (gomp_load_plugin_for_device): Initialize async_run. * task.c (gomp_init_task): Initialize children_queue. (gomp_clear_parent_in_list): New. (gomp_clear_parent_in_tree): New. (gomp_clear_parent): Handle priorities. (GOMP_task): Likewise. (priority_queue_move_task_first, gomp_target_task_completion, GOMP_PLUGIN_target_task_completion): New functions. (gomp_create_target_task): Use priority queues. Change return type to bool, add state argument, return false if for async {{enter,exit} data,update} constructs no dependencies need to be waited for, handle target nowait. Set task->fn to NULL instead of gomp_target_task_fn. (verify_children_queue): Remove. (priority_list_upgrade_task): New. (priority_queue_upgrade_task): New. (verify_task_queue): Remove. (priority_list_downgrade_task): New. (priority_queue_downgrade_task): New. (gomp_task_run_pre): Use priority queues. Abstract code out to priority_queue_downgrade_task. (gomp_task_run_post_handle_dependers): Use priority queues. (gomp_task_run_post_remove_parent): Likewise. (gomp_task_run_post_remove_taskgroup): Likewise. (gomp_barrier_handle_tasks): Likewise. Handle target nowait target tasks specially. (GOMP_taskwait): Likewise. (gomp_task_maybe_wait_for_dependencies): Likewise. Abstract code to priority-queue_upgrade_task. (GOMP_taskgroup_start): Use priority queues. (GOMP_taskgroup_end): Likewise. Handle target nowait target tasks specially. If taskgroup is NULL, and thr->ts.level is 0, act as a barrier. * taskloop.c (GOMP_taskloop): Handle priorities. * team.c (gomp_new_team): Call priority_queue_init. (free_team): Call priority_queue_free. (gomp_free_thread): Call gomp_team_end if thr->ts.team is artificial team created for target nowait in implicit parallel region. (gomp_team_start): For nested check, test thr->ts.level instead of thr->ts.team != NULL. * testsuite/libgomp.c/doacross-3.c: New test. * testsuite/libgomp.c/ordered-5.c: New test. * testsuite/libgomp.c/priority.c: New test. * testsuite/libgomp.c/target-31.c: New test. * testsuite/libgomp.c/target-32.c: New test. * testsuite/libgomp.c/target-33.c: New test. * testsuite/libgomp.c/target-34.c: New test. liboffloadmic/ 2015-11-14 Ilya Verbin <ilya.verbin@intel.com> * runtime/offload_host.cpp (task_completion_callback): New variable. (offload_proxy_task_completed_ooo): Call task_completion_callback. (__offload_register_task_callback): New function. * runtime/offload_host.h (__offload_register_task_callback): New declaration. * plugin/libgomp-plugin-intelmic.cpp (offload): Add async_data argument, handle async offloading. (register_main_image): Call register_main_image. (GOMP_OFFLOAD_init_device, get_target_table, GOMP_OFFLOAD_alloc, GOMP_OFFLOAD_free, GOMP_OFFLOAD_host2dev, GOMP_OFFLOAD_dev2host, GOMP_OFFLOAD_dev2dev) Adjust offload callers. (GOMP_OFFLOAD_async_run): New function. (GOMP_OFFLOAD_run): Implement using GOMP_OFFLOAD_async_run. From-SVN: r230381
2015-11-14 19:42:13 +01:00
Contributed by Aldy Hernandez <aldyh@redhat.com>.
This file is part of the GNU Offloading and Multi Processing Library
(libgomp).
Libgomp is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
/* Priority queue implementation of GOMP tasks. */
#include "libgomp.h"
#if _LIBGOMP_CHECKING_
#include <stdio.h>
/* Sanity check to verify whether a TASK is in LIST. Return TRUE if
found, FALSE otherwise.
TYPE is the type of priority queue this task resides in. */
static inline bool
priority_queue_task_in_list_p (enum priority_queue_type type,
struct priority_list *list,
struct gomp_task *task)
{
struct priority_node *p = list->tasks;
do
{
if (priority_node_to_task (type, p) == task)
return true;
p = p->next;
}
while (p != list->tasks);
return false;
}
/* Tree version of priority_queue_task_in_list_p. */
static inline bool
priority_queue_task_in_tree_p (enum priority_queue_type type,
struct priority_queue *head,
struct gomp_task *task)
{
struct priority_list *list
= priority_queue_lookup_priority (head, task->priority);
if (!list)
return false;
return priority_queue_task_in_list_p (type, list, task);
}
/* Generic version of priority_queue_task_in_list_p that works for
trees or lists. */
bool
priority_queue_task_in_queue_p (enum priority_queue_type type,
struct priority_queue *head,
struct gomp_task *task)
{
if (priority_queue_empty_p (head, MEMMODEL_RELAXED))
return false;
if (priority_queue_multi_p (head))
return priority_queue_task_in_tree_p (type, head, task);
else
return priority_queue_task_in_list_p (type, &head->l, task);
}
/* Sanity check LIST to make sure the tasks therein are in the right
order. LIST is a priority list of type TYPE.
The expected order is that GOMP_TASK_WAITING tasks come before
GOMP_TASK_TIED/GOMP_TASK_ASYNC_RUNNING ones.
If CHECK_DEPS is TRUE, we also check that parent_depends_on WAITING
tasks come before !parent_depends_on WAITING tasks. This is only
applicable to the children queue, and the caller is expected to
ensure that we are verifying the children queue. */
static void
priority_list_verify (enum priority_queue_type type,
struct priority_list *list, bool check_deps)
{
bool seen_tied = false;
bool seen_plain_waiting = false;
struct priority_node *p = list->tasks;
while (1)
{
struct gomp_task *t = priority_node_to_task (type, p);
if (seen_tied && t->kind == GOMP_TASK_WAITING)
gomp_fatal ("priority_queue_verify: WAITING task after TIED");
if (t->kind >= GOMP_TASK_TIED)
seen_tied = true;
else if (check_deps && t->kind == GOMP_TASK_WAITING)
{
if (t->parent_depends_on)
{
if (seen_plain_waiting)
gomp_fatal ("priority_queue_verify: "
"parent_depends_on after !parent_depends_on");
}
else
seen_plain_waiting = true;
}
p = p->next;
if (p == list->tasks)
break;
}
}
/* Callback type for priority_tree_verify_callback. */
struct cbtype
{
enum priority_queue_type type;
bool check_deps;
};
/* Verify every task in NODE.
Callback for splay_tree_foreach. */
static void
priority_tree_verify_callback (prio_splay_tree_key key, void *data)
{
struct cbtype *cb = (struct cbtype *) data;
priority_list_verify (cb->type, &key->l, cb->check_deps);
}
/* Generic version of priority_list_verify.
Sanity check HEAD to make sure the tasks therein are in the right
order. The priority_queue holds tasks of type TYPE.
If CHECK_DEPS is TRUE, we also check that parent_depends_on WAITING
tasks come before !parent_depends_on WAITING tasks. This is only
applicable to the children queue, and the caller is expected to
ensure that we are verifying the children queue. */
void
priority_queue_verify (enum priority_queue_type type,
struct priority_queue *head, bool check_deps)
{
if (priority_queue_empty_p (head, MEMMODEL_RELAXED))
return;
if (priority_queue_multi_p (head))
{
struct cbtype cb = { type, check_deps };
prio_splay_tree_foreach (&head->t,
priority_tree_verify_callback, &cb);
}
else
priority_list_verify (type, &head->l, check_deps);
}
#endif /* _LIBGOMP_CHECKING_ */
/* Remove NODE from priority queue HEAD, wherever it may be inside the
tree. HEAD contains tasks of type TYPE. */
void
priority_tree_remove (enum priority_queue_type type,
struct priority_queue *head,
struct priority_node *node)
{
/* ?? The only reason this function is not inlined is because we
need to find the priority within gomp_task (which has not been
completely defined in the header file). If the lack of inlining
is a concern, we could pass the priority number as a
parameter, or we could move this to libgomp.h. */
int priority = priority_node_to_task (type, node)->priority;
/* ?? We could avoid this lookup by keeping a pointer to the key in
the priority_node. */
struct priority_list *list
= priority_queue_lookup_priority (head, priority);
#if _LIBGOMP_CHECKING_
if (!list)
gomp_fatal ("Unable to find priority %d", priority);
#endif
/* If NODE was the last in its priority, clean up the priority. */
if (priority_list_remove (list, node, MEMMODEL_RELAXED))
{
prio_splay_tree_remove (&head->t, (prio_splay_tree_key) list);
list->tasks = NULL;
#if _LIBGOMP_CHECKING_
memset (list, 0xaf, sizeof (*list));
#endif
free (list);
}
}
/* Return the highest priority WAITING task in a splay tree NODE. If
there are no WAITING tasks available, return NULL.
NODE is a priority list containing tasks of type TYPE.
The right most node in a tree contains the highest priority.
Recurse down to find such a node. If the task at that max node is
not WAITING, bubble back up and look at the remaining tasks
in-order. */
static struct gomp_task *
priority_tree_next_task_1 (enum priority_queue_type type,
prio_splay_tree_node node)
{
again:
if (!node)
return NULL;
struct gomp_task *ret = priority_tree_next_task_1 (type, node->right);
if (ret)
return ret;
ret = priority_node_to_task (type, node->key.l.tasks);
if (ret->kind == GOMP_TASK_WAITING)
return ret;
node = node->left;
goto again;
}
/* Return the highest priority WAITING task from within Q1 and Q2,
while giving preference to tasks from Q1. Q1 is a queue containing
items of type TYPE1. Q2 is a queue containing items of type TYPE2.
Since we are mostly interested in Q1, if there are no WAITING tasks
in Q1, we don't bother checking Q2, and just return NULL.
As a special case, Q2 can be NULL, in which case, we just choose
the highest priority WAITING task in Q1. This is an optimization
to speed up looking through only one queue.
If the returned task is chosen from Q1, *Q1_CHOSEN_P is set to
TRUE, otherwise it is set to FALSE. */
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)
{
struct gomp_task *t1 = priority_tree_next_task_1 (type1, q1->t.root);
if (!t1
/* Special optimization when only searching through one queue. */
|| !q2)
{
*q1_chosen_p = true;
return t1;
}
struct gomp_task *t2 = priority_tree_next_task_1 (type2, q2->t.root);
if (!t2 || t1->priority > t2->priority)
{
*q1_chosen_p = true;
return t1;
}
if (t2->priority > t1->priority)
{
*q1_chosen_p = false;
return t2;
}
/* If we get here, the priorities are the same, so we must look at
parent_depends_on to make our decision. */
#if _LIBGOMP_CHECKING_
if (t1 != t2)
gomp_fatal ("priority_tree_next_task: t1 != t2");
#endif
if (t2->parent_depends_on && !t1->parent_depends_on)
{
*q1_chosen_p = false;
return t2;
}
*q1_chosen_p = true;
return t1;
}
/* Priority splay trees comparison function. */
static inline int
prio_splay_compare (prio_splay_tree_key x, prio_splay_tree_key y)
{
if (x->l.priority == y->l.priority)
return 0;
return x->l.priority < y->l.priority ? -1 : 1;
}
/* Define another splay tree instantiation, for priority_list's. */
#define splay_tree_prefix prio
#define splay_tree_c
#include "splay-tree.h"