123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954 |
- /* OpenACC Runtime initialization routines
- Copyright (C) 2013-2022 Free Software Foundation, Inc.
- Contributed by Mentor Embedded.
- 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/>. */
- #include "libgomp.h"
- #include "oacc-int.h"
- #include "openacc.h"
- #include <assert.h>
- #include <stdlib.h>
- #include <strings.h>
- #include <stdbool.h>
- #include <string.h>
- /* This lock is used to protect access to cached_base_dev, dispatchers and
- the (abstract) initialisation state of attached offloading devices. */
- static gomp_mutex_t acc_device_lock;
- static gomp_mutex_t acc_init_state_lock;
- static enum { uninitialized, initializing, initialized } acc_init_state
- = uninitialized;
- static pthread_t acc_init_thread;
- /* A cached version of the dispatcher for the global "current" accelerator type,
- e.g. used as the default when creating new host threads. This is the
- device-type equivalent of goacc_device_num (which specifies which device to
- use out of potentially several of the same type). If there are several
- devices of a given type, this points at the first one. */
- static struct gomp_device_descr *cached_base_dev = NULL;
- #if defined HAVE_TLS || defined USE_EMUTLS
- __thread struct goacc_thread *goacc_tls_data;
- #else
- pthread_key_t goacc_tls_key;
- #endif
- static pthread_key_t goacc_cleanup_key;
- static struct goacc_thread *goacc_threads;
- static gomp_mutex_t goacc_thread_lock;
- /* An array of dispatchers for device types, indexed by the type. This array
- only references "base" devices, and other instances of the same type are
- found by simply indexing from each such device (which are stored linearly,
- grouped by device in target.c:devices). */
- static struct gomp_device_descr *dispatchers[_ACC_device_hwm] = { 0 };
- attribute_hidden void
- goacc_register (struct gomp_device_descr *disp)
- {
- /* Only register the 0th device here. */
- if (disp->target_id != 0)
- return;
- gomp_mutex_lock (&acc_device_lock);
- assert (acc_device_type (disp->type) != acc_device_none
- && acc_device_type (disp->type) != acc_device_default
- && acc_device_type (disp->type) != acc_device_not_host);
- assert (!dispatchers[disp->type]);
- dispatchers[disp->type] = disp;
- gomp_mutex_unlock (&acc_device_lock);
- }
- static bool
- known_device_type_p (acc_device_t d)
- {
- return d >= 0 && d < _ACC_device_hwm;
- }
- static void
- unknown_device_type_error (acc_device_t invalid_type)
- {
- gomp_fatal ("unknown device type %u", invalid_type);
- }
- /* OpenACC names some things a little differently. */
- static const char *
- get_openacc_name (const char *name)
- {
- if (strcmp (name, "gcn") == 0)
- return "radeon";
- else if (strcmp (name, "nvptx") == 0)
- return "nvidia";
- else
- return name;
- }
- static const char *
- name_of_acc_device_t (enum acc_device_t type)
- {
- switch (type)
- {
- case acc_device_none: return "none";
- case acc_device_default: return "default";
- case acc_device_host: return "host";
- case acc_device_not_host: return "not_host";
- case acc_device_nvidia: return "nvidia";
- case acc_device_radeon: return "radeon";
- default: unknown_device_type_error (type);
- }
- __builtin_unreachable ();
- }
- /* ACC_DEVICE_LOCK must be held before calling this function. If FAIL_IS_ERROR
- is true, this function raises an error if there are no devices of type D,
- otherwise it returns NULL in that case. */
- static struct gomp_device_descr *
- resolve_device (acc_device_t d, bool fail_is_error)
- {
- acc_device_t d_arg = d;
- switch (d)
- {
- case acc_device_default:
- {
- if (goacc_device_type)
- {
- /* Lookup the named device. */
- while (known_device_type_p (++d))
- if (dispatchers[d]
- && !strcasecmp (goacc_device_type,
- get_openacc_name (dispatchers[d]->name))
- && dispatchers[d]->get_num_devices_func () > 0)
- goto found;
- if (fail_is_error)
- {
- gomp_mutex_unlock (&acc_device_lock);
- gomp_fatal ("device type %s not supported", goacc_device_type);
- }
- else
- return NULL;
- }
- /* No default device specified, so start scanning for any non-host
- device that is available. */
- d = acc_device_not_host;
- }
- /* FALLTHROUGH */
- case acc_device_not_host:
- /* Find the first available device after acc_device_not_host. */
- while (known_device_type_p (++d))
- if (dispatchers[d] && dispatchers[d]->get_num_devices_func () > 0)
- goto found;
- if (d_arg == acc_device_default)
- {
- d = acc_device_host;
- goto found;
- }
- if (fail_is_error)
- {
- gomp_mutex_unlock (&acc_device_lock);
- gomp_fatal ("no device found");
- }
- else
- return NULL;
- break;
- case acc_device_host:
- break;
- default:
- if (!known_device_type_p (d))
- {
- if (fail_is_error)
- goto unsupported_device;
- else
- return NULL;
- }
- break;
- }
- found:
- assert (d != acc_device_none
- && d != acc_device_default
- && d != acc_device_not_host);
- if (dispatchers[d] == NULL && fail_is_error)
- {
- unsupported_device:
- gomp_mutex_unlock (&acc_device_lock);
- gomp_fatal ("device type %s not supported", name_of_acc_device_t (d));
- }
- return dispatchers[d];
- }
- /* Emit a suitable error if no device of a particular type is available, or
- the given device number is out-of-range. */
- static void
- acc_dev_num_out_of_range (acc_device_t d, int ord, int ndevs)
- {
- if (ndevs == 0)
- gomp_fatal ("no devices of type %s available", name_of_acc_device_t (d));
- else
- gomp_fatal ("device %u out of range", ord);
- }
- /* This is called when plugins have been initialized, and serves to call
- (indirectly) the target's device_init hook. Calling multiple times without
- an intervening acc_shutdown_1 call is an error. ACC_DEVICE_LOCK must be
- held before calling this function. */
- static struct gomp_device_descr *
- acc_init_1 (acc_device_t d, acc_construct_t parent_construct, int implicit)
- {
- gomp_mutex_lock (&acc_init_state_lock);
- acc_init_state = initializing;
- acc_init_thread = pthread_self ();
- gomp_mutex_unlock (&acc_init_state_lock);
- bool check_not_nested_p;
- if (implicit)
- {
- /* In the implicit case, there should (TODO: must?) already be something
- have been set up for an outer construct. */
- check_not_nested_p = false;
- }
- else
- {
- check_not_nested_p = true;
- /* TODO: should we set 'thr->prof_info' etc. in this case ('acc_init')?
- The problem is, that we don't have 'thr' yet? (So,
- 'check_not_nested_p = true' also is pointless actually.) */
- }
- bool profiling_p = GOACC_PROFILING_DISPATCH_P (check_not_nested_p);
- acc_prof_info prof_info;
- if (profiling_p)
- {
- prof_info.event_type = acc_ev_device_init_start;
- prof_info.valid_bytes = _ACC_PROF_INFO_VALID_BYTES;
- prof_info.version = _ACC_PROF_INFO_VERSION;
- prof_info.device_type = d;
- prof_info.device_number = goacc_device_num;
- prof_info.thread_id = -1;
- prof_info.async = acc_async_sync;
- prof_info.async_queue = prof_info.async;
- prof_info.src_file = NULL;
- prof_info.func_name = NULL;
- prof_info.line_no = -1;
- prof_info.end_line_no = -1;
- prof_info.func_line_no = -1;
- prof_info.func_end_line_no = -1;
- }
- acc_event_info device_init_event_info;
- if (profiling_p)
- {
- device_init_event_info.other_event.event_type = prof_info.event_type;
- device_init_event_info.other_event.valid_bytes
- = _ACC_OTHER_EVENT_INFO_VALID_BYTES;
- device_init_event_info.other_event.parent_construct = parent_construct;
- device_init_event_info.other_event.implicit = implicit;
- device_init_event_info.other_event.tool_info = NULL;
- }
- acc_api_info api_info;
- if (profiling_p)
- {
- api_info.device_api = acc_device_api_none;
- api_info.valid_bytes = _ACC_API_INFO_VALID_BYTES;
- api_info.device_type = prof_info.device_type;
- api_info.vendor = -1;
- api_info.device_handle = NULL;
- api_info.context_handle = NULL;
- api_info.async_handle = NULL;
- }
- if (profiling_p)
- goacc_profiling_dispatch (&prof_info, &device_init_event_info, &api_info);
- struct gomp_device_descr *base_dev, *acc_dev;
- int ndevs;
- base_dev = resolve_device (d, true);
- ndevs = base_dev->get_num_devices_func ();
- if (ndevs <= 0 || goacc_device_num >= ndevs)
- acc_dev_num_out_of_range (d, goacc_device_num, ndevs);
- acc_dev = &base_dev[goacc_device_num];
- gomp_mutex_lock (&acc_dev->lock);
- if (acc_dev->state == GOMP_DEVICE_INITIALIZED)
- {
- gomp_mutex_unlock (&acc_dev->lock);
- gomp_fatal ("device already active");
- }
- gomp_init_device (acc_dev);
- gomp_mutex_unlock (&acc_dev->lock);
- if (profiling_p)
- {
- prof_info.event_type = acc_ev_device_init_end;
- device_init_event_info.other_event.event_type = prof_info.event_type;
- goacc_profiling_dispatch (&prof_info, &device_init_event_info,
- &api_info);
- }
- /* We're setting 'initialized' *after* 'goacc_profiling_dispatch', so that a
- nested 'acc_get_device_type' called from a profiling callback still sees
- 'initializing', so that we don't deadlock when it then again tries to lock
- 'goacc_prof_lock'. See also the discussion in 'acc_get_device_type'. */
- gomp_mutex_lock (&acc_init_state_lock);
- acc_init_state = initialized;
- gomp_mutex_unlock (&acc_init_state_lock);
- return base_dev;
- }
- /* ACC_DEVICE_LOCK must be held before calling this function. */
- static void
- acc_shutdown_1 (acc_device_t d)
- {
- struct gomp_device_descr *base_dev;
- struct goacc_thread *walk;
- int ndevs, i;
- bool devices_active = false;
- /* Get the base device for this device type. */
- base_dev = resolve_device (d, true);
- ndevs = base_dev->get_num_devices_func ();
- /* Unload all the devices of this type that have been opened. */
- for (i = 0; i < ndevs; i++)
- {
- struct gomp_device_descr *acc_dev = &base_dev[i];
- gomp_mutex_lock (&acc_dev->lock);
- gomp_unload_device (acc_dev);
- gomp_mutex_unlock (&acc_dev->lock);
- }
-
- gomp_mutex_lock (&goacc_thread_lock);
- /* Free target-specific TLS data and close all devices. */
- for (walk = goacc_threads; walk != NULL; walk = walk->next)
- {
- if (walk->target_tls)
- base_dev->openacc.destroy_thread_data_func (walk->target_tls);
- walk->target_tls = NULL;
- /* This would mean the user is shutting down OpenACC in the middle of an
- "acc data" pragma. Likely not intentional. */
- if (walk->mapped_data)
- {
- gomp_mutex_unlock (&goacc_thread_lock);
- gomp_fatal ("shutdown in 'acc data' region");
- }
- /* Similarly, if this happens then user code has done something weird. */
- if (walk->saved_bound_dev)
- {
- gomp_mutex_unlock (&goacc_thread_lock);
- gomp_fatal ("shutdown during host fallback");
- }
- if (walk->dev)
- {
- gomp_mutex_lock (&walk->dev->lock);
- while (walk->dev->mem_map.root)
- {
- splay_tree_key k = &walk->dev->mem_map.root->key;
- if (k->aux)
- k->aux->link_key = NULL;
- gomp_remove_var (walk->dev, k);
- }
- gomp_mutex_unlock (&walk->dev->lock);
- walk->dev = NULL;
- walk->base_dev = NULL;
- }
- }
- gomp_mutex_unlock (&goacc_thread_lock);
- /* Close all the devices of this type that have been opened. */
- bool ret = true;
- for (i = 0; i < ndevs; i++)
- {
- struct gomp_device_descr *acc_dev = &base_dev[i];
- gomp_mutex_lock (&acc_dev->lock);
- if (acc_dev->state == GOMP_DEVICE_INITIALIZED)
- {
- devices_active = true;
- ret &= gomp_fini_device (acc_dev);
- acc_dev->state = GOMP_DEVICE_UNINITIALIZED;
- }
- gomp_mutex_unlock (&acc_dev->lock);
- }
- if (!ret)
- gomp_fatal ("device finalization failed");
- if (!devices_active)
- gomp_fatal ("no device initialized");
- }
- static struct goacc_thread *
- goacc_new_thread (void)
- {
- struct goacc_thread *thr = gomp_malloc (sizeof (struct goacc_thread));
- #if defined HAVE_TLS || defined USE_EMUTLS
- goacc_tls_data = thr;
- #else
- pthread_setspecific (goacc_tls_key, thr);
- #endif
- pthread_setspecific (goacc_cleanup_key, thr);
- gomp_mutex_lock (&goacc_thread_lock);
- thr->next = goacc_threads;
- goacc_threads = thr;
- gomp_mutex_unlock (&goacc_thread_lock);
- return thr;
- }
- static void
- goacc_destroy_thread (void *data)
- {
- struct goacc_thread *thr = data, *walk, *prev;
- gomp_mutex_lock (&goacc_thread_lock);
- if (thr)
- {
- struct gomp_device_descr *acc_dev = thr->dev;
- if (acc_dev && thr->target_tls)
- {
- acc_dev->openacc.destroy_thread_data_func (thr->target_tls);
- thr->target_tls = NULL;
- }
- assert (!thr->mapped_data);
- /* Remove from thread list. */
- for (prev = NULL, walk = goacc_threads; walk;
- prev = walk, walk = walk->next)
- if (walk == thr)
- {
- if (prev == NULL)
- goacc_threads = walk->next;
- else
- prev->next = walk->next;
- free (thr);
- break;
- }
- assert (walk);
- }
- gomp_mutex_unlock (&goacc_thread_lock);
- }
- /* Use the ORD'th device instance for the current host thread (or -1 for the
- current global default). The device (and the runtime) must be initialised
- before calling this function. */
- void
- goacc_attach_host_thread_to_device (int ord)
- {
- struct goacc_thread *thr = goacc_thread ();
- struct gomp_device_descr *acc_dev = NULL, *base_dev = NULL;
- int num_devices;
-
- if (thr && thr->dev && (thr->dev->target_id == ord || ord < 0))
- return;
-
- if (ord < 0)
- ord = goacc_device_num;
-
- /* Decide which type of device to use. If the current thread has a device
- type already (e.g. set by acc_set_device_type), use that, else use the
- global default. */
- if (thr && thr->base_dev)
- base_dev = thr->base_dev;
- else
- {
- assert (cached_base_dev);
- base_dev = cached_base_dev;
- }
-
- num_devices = base_dev->get_num_devices_func ();
- if (num_devices <= 0 || ord >= num_devices)
- acc_dev_num_out_of_range (acc_device_type (base_dev->type), ord,
- num_devices);
-
- if (!thr)
- thr = goacc_new_thread ();
-
- thr->base_dev = base_dev;
- thr->dev = acc_dev = &base_dev[ord];
- thr->saved_bound_dev = NULL;
- thr->mapped_data = NULL;
- thr->prof_info = NULL;
- thr->api_info = NULL;
- /* Initially, all callbacks for all events are enabled. */
- thr->prof_callbacks_enabled = true;
- thr->target_tls
- = acc_dev->openacc.create_thread_data_func (ord);
- }
- /* OpenACC 2.0a (3.2.12, 3.2.13) doesn't specify whether the serialization of
- init/shutdown is per-process or per-thread. We choose per-process. */
- void
- acc_init (acc_device_t d)
- {
- if (!known_device_type_p (d))
- unknown_device_type_error (d);
- gomp_init_targets_once ();
- gomp_mutex_lock (&acc_device_lock);
- cached_base_dev = acc_init_1 (d, acc_construct_runtime_api, 0);
- gomp_mutex_unlock (&acc_device_lock);
-
- goacc_attach_host_thread_to_device (-1);
- }
- ialias (acc_init)
- void
- acc_shutdown (acc_device_t d)
- {
- if (!known_device_type_p (d))
- unknown_device_type_error (d);
- gomp_init_targets_once ();
- gomp_mutex_lock (&acc_device_lock);
- acc_shutdown_1 (d);
- gomp_mutex_unlock (&acc_device_lock);
- }
- ialias (acc_shutdown)
- int
- acc_get_num_devices (acc_device_t d)
- {
- if (!known_device_type_p (d))
- unknown_device_type_error (d);
- int n = 0;
- struct gomp_device_descr *acc_dev;
- if (d == acc_device_none)
- return 0;
- gomp_init_targets_once ();
- gomp_mutex_lock (&acc_device_lock);
- acc_dev = resolve_device (d, false);
- gomp_mutex_unlock (&acc_device_lock);
- if (!acc_dev)
- return 0;
- n = acc_dev->get_num_devices_func ();
- if (n < 0)
- n = 0;
- return n;
- }
- ialias (acc_get_num_devices)
- /* Set the device type for the current thread only (using the current global
- default device number), initialising that device if necessary. Also set the
- default device type for new threads to D. */
- void
- acc_set_device_type (acc_device_t d)
- {
- if (!known_device_type_p (d))
- unknown_device_type_error (d);
- struct gomp_device_descr *base_dev, *acc_dev;
- struct goacc_thread *thr = goacc_thread ();
- acc_prof_info prof_info;
- acc_api_info api_info;
- bool profiling_p = GOACC_PROFILING_SETUP_P (thr, &prof_info, &api_info);
- if (profiling_p)
- prof_info.device_type = d;
- gomp_init_targets_once ();
- gomp_mutex_lock (&acc_device_lock);
- cached_base_dev = base_dev = resolve_device (d, true);
- acc_dev = &base_dev[goacc_device_num];
- gomp_mutex_lock (&acc_dev->lock);
- if (acc_dev->state == GOMP_DEVICE_UNINITIALIZED)
- gomp_init_device (acc_dev);
- gomp_mutex_unlock (&acc_dev->lock);
- gomp_mutex_unlock (&acc_device_lock);
- /* We're changing device type: invalidate the current thread's dev and
- base_dev pointers. */
- if (thr && thr->base_dev != base_dev)
- {
- thr->base_dev = thr->dev = NULL;
- if (thr->mapped_data)
- gomp_fatal ("acc_set_device_type in 'acc data' region");
- }
- goacc_attach_host_thread_to_device (-1);
- if (profiling_p)
- {
- thr->prof_info = NULL;
- thr->api_info = NULL;
- }
- }
- ialias (acc_set_device_type)
- static bool
- self_initializing_p (void)
- {
- bool res;
- gomp_mutex_lock (&acc_init_state_lock);
- res = (acc_init_state == initializing
- && pthread_equal (acc_init_thread, pthread_self ()));
- gomp_mutex_unlock (&acc_init_state_lock);
- return res;
- }
- acc_device_t
- acc_get_device_type (void)
- {
- acc_device_t res = acc_device_none;
- struct gomp_device_descr *dev;
- struct goacc_thread *thr = goacc_thread ();
- if (thr && thr->base_dev)
- res = acc_device_type (thr->base_dev->type);
- else if (self_initializing_p ())
- /* The Cuda libaccinj64.so version 9.0+ calls acc_get_device_type during the
- acc_ev_device_init_start event callback, which is dispatched during
- acc_init_1. Trying to lock acc_device_lock during such a call (as we do
- in the else clause below), will result in deadlock, since the lock has
- already been taken by the acc_init_1 caller. We work around this problem
- by using the acc_get_device_type property "If the device type has not yet
- been selected, the value acc_device_none may be returned". */
- ;
- else
- {
- acc_prof_info prof_info;
- acc_api_info api_info;
- bool profiling_p = GOACC_PROFILING_SETUP_P (thr, &prof_info, &api_info);
- gomp_init_targets_once ();
- gomp_mutex_lock (&acc_device_lock);
- dev = resolve_device (acc_device_default, true);
- gomp_mutex_unlock (&acc_device_lock);
- res = acc_device_type (dev->type);
- if (profiling_p)
- {
- thr->prof_info = NULL;
- thr->api_info = NULL;
- }
- }
- assert (res != acc_device_default
- && res != acc_device_not_host
- && res != acc_device_current);
- return res;
- }
- ialias (acc_get_device_type)
- int
- acc_get_device_num (acc_device_t d)
- {
- if (!known_device_type_p (d))
- unknown_device_type_error (d);
- const struct gomp_device_descr *dev;
- struct goacc_thread *thr = goacc_thread ();
- acc_prof_info prof_info;
- acc_api_info api_info;
- bool profiling_p = GOACC_PROFILING_SETUP_P (thr, &prof_info, &api_info);
- if (profiling_p)
- prof_info.device_type = d;
- gomp_init_targets_once ();
- gomp_mutex_lock (&acc_device_lock);
- dev = resolve_device (d, true);
- gomp_mutex_unlock (&acc_device_lock);
- if (profiling_p)
- {
- thr->prof_info = NULL;
- thr->api_info = NULL;
- }
- if (thr && thr->base_dev == dev && thr->dev)
- return thr->dev->target_id;
- return goacc_device_num;
- }
- ialias (acc_get_device_num)
- void
- acc_set_device_num (int ord, acc_device_t d)
- {
- if (!known_device_type_p (d))
- unknown_device_type_error (d);
- struct gomp_device_descr *base_dev, *acc_dev;
- int num_devices;
- gomp_init_targets_once ();
- if (ord < 0)
- ord = goacc_device_num;
- if ((int) d == 0)
- /* Set whatever device is being used by the current host thread to use
- device instance ORD. It's unclear if this is supposed to affect other
- host threads too (OpenACC 2.0 (3.2.4) acc_set_device_num). */
- goacc_attach_host_thread_to_device (ord);
- else
- {
- gomp_mutex_lock (&acc_device_lock);
- cached_base_dev = base_dev = resolve_device (d, true);
- num_devices = base_dev->get_num_devices_func ();
- if (num_devices <= 0 || ord >= num_devices)
- acc_dev_num_out_of_range (d, ord, num_devices);
- acc_dev = &base_dev[ord];
- gomp_mutex_lock (&acc_dev->lock);
- if (acc_dev->state == GOMP_DEVICE_UNINITIALIZED)
- gomp_init_device (acc_dev);
- gomp_mutex_unlock (&acc_dev->lock);
- gomp_mutex_unlock (&acc_device_lock);
- goacc_attach_host_thread_to_device (ord);
- }
-
- goacc_device_num = ord;
- }
- ialias (acc_set_device_num)
- static union goacc_property_value
- get_property_any (int ord, acc_device_t d, acc_device_property_t prop)
- {
- goacc_lazy_initialize ();
- struct goacc_thread *thr = goacc_thread ();
- if (d == acc_device_current && thr && thr->dev)
- return thr->dev->openacc.get_property_func (thr->dev->target_id, prop);
- gomp_mutex_lock (&acc_device_lock);
- struct gomp_device_descr *dev = resolve_device (d, true);
- int num_devices = dev->get_num_devices_func ();
- if (num_devices <= 0 || ord >= num_devices)
- acc_dev_num_out_of_range (d, ord, num_devices);
- dev += ord;
- gomp_mutex_lock (&dev->lock);
- if (dev->state == GOMP_DEVICE_UNINITIALIZED)
- gomp_init_device (dev);
- gomp_mutex_unlock (&dev->lock);
- gomp_mutex_unlock (&acc_device_lock);
- assert (dev);
- return dev->openacc.get_property_func (dev->target_id, prop);
- }
- size_t
- acc_get_property (int ord, acc_device_t d, acc_device_property_t prop)
- {
- if (!known_device_type_p (d))
- unknown_device_type_error(d);
- if (prop & GOACC_PROPERTY_STRING_MASK)
- return 0;
- else
- return get_property_any (ord, d, prop).val;
- }
- ialias (acc_get_property)
- const char *
- acc_get_property_string (int ord, acc_device_t d, acc_device_property_t prop)
- {
- if (!known_device_type_p (d))
- unknown_device_type_error(d);
- if (prop & GOACC_PROPERTY_STRING_MASK)
- return get_property_any (ord, d, prop).ptr;
- else
- return NULL;
- }
- ialias (acc_get_property_string)
- /* For -O and higher, the compiler always attempts to expand acc_on_device, but
- if the user disables the builtin, or calls it via a pointer, we'll need this
- version.
- Compile this with optimization, so that the compiler expands
- this, rather than generating infinitely recursive code.
- The function just forwards its argument to __builtin_acc_on_device. It does
- not verify that the argument is a valid acc_device_t enumeration value. */
- int __attribute__ ((__optimize__ ("O2")))
- acc_on_device (acc_device_t dev)
- {
- return __builtin_acc_on_device (dev);
- }
- ialias (acc_on_device)
- attribute_hidden void
- goacc_runtime_initialize (void)
- {
- gomp_mutex_init (&acc_device_lock);
- #if !(defined HAVE_TLS || defined USE_EMUTLS)
- pthread_key_create (&goacc_tls_key, NULL);
- #endif
- pthread_key_create (&goacc_cleanup_key, goacc_destroy_thread);
- cached_base_dev = NULL;
- goacc_threads = NULL;
- gomp_mutex_init (&goacc_thread_lock);
- /* Initialize and register the 'host' device type. */
- goacc_host_init ();
- }
- static void __attribute__((destructor))
- goacc_runtime_deinitialize (void)
- {
- #if !(defined HAVE_TLS || defined USE_EMUTLS)
- pthread_key_delete (goacc_tls_key);
- #endif
- pthread_key_delete (goacc_cleanup_key);
- }
- /* Compiler helper functions */
- attribute_hidden void
- goacc_save_and_set_bind (acc_device_t d)
- {
- struct goacc_thread *thr = goacc_thread ();
- assert (!thr->saved_bound_dev);
- thr->saved_bound_dev = thr->dev;
- thr->dev = dispatchers[d];
- }
- attribute_hidden void
- goacc_restore_bind (void)
- {
- struct goacc_thread *thr = goacc_thread ();
- thr->dev = thr->saved_bound_dev;
- thr->saved_bound_dev = NULL;
- }
- /* This is called from any OpenACC support function that may need to implicitly
- initialize the libgomp runtime, either globally or from a new host thread.
- On exit "goacc_thread" will return a valid & populated thread block. */
- attribute_hidden void
- goacc_lazy_initialize (void)
- {
- struct goacc_thread *thr = goacc_thread ();
- if (thr && thr->dev)
- return;
- gomp_init_targets_once ();
- gomp_mutex_lock (&acc_device_lock);
- if (!cached_base_dev)
- cached_base_dev = acc_init_1 (acc_device_default,
- acc_construct_parallel, 1);
- gomp_mutex_unlock (&acc_device_lock);
- goacc_attach_host_thread_to_device (-1);
- }
|