Newer
Older
/* OpenACC Runtime initialization routines
Copyright (C) 2013-2023 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 "openacc.h"
#include "libgomp.h"
#include "gomp-constants.h"
#include "oacc-int.h"
Cesar Philippidis
committed
#include <string.h>
#include <assert.h>
Julian Brown
committed
/* Return block containing [H->S), or NULL if not contained. The device lock
for DEV must be locked on entry, and remains locked on exit. */
static splay_tree_key
lookup_host (struct gomp_device_descr *dev, void *h, size_t s)
{
struct splay_tree_key_s node;
splay_tree_key key;
node.host_start = (uintptr_t) h;
node.host_end = (uintptr_t) h + s;
key = splay_tree_lookup (&dev->mem_map, &node);
return key;
}
Thomas Schwinge
committed
/* Helper for lookup_dev. Iterate over splay tree. */
static splay_tree_key
Thomas Schwinge
committed
lookup_dev_1 (splay_tree_node node, uintptr_t d, size_t s)
Thomas Schwinge
committed
splay_tree_key key = &node->key;
if (d >= key->tgt->tgt_start && d + s <= key->tgt->tgt_end)
return key;
Thomas Schwinge
committed
key = NULL;
if (node->left)
key = lookup_dev_1 (node->left, d, s);
if (!key && node->right)
key = lookup_dev_1 (node->right, d, s);
Thomas Schwinge
committed
return key;
}
Thomas Schwinge
committed
/* Return block containing [D->S), or NULL if not contained.
Thomas Schwinge
committed
This iterates over the splay tree. This is not expected to be a common
operation.
Thomas Schwinge
committed
The device lock associated with MEM_MAP must be locked on entry, and remains
locked on exit. */
Thomas Schwinge
committed
static splay_tree_key
lookup_dev (splay_tree mem_map, void *d, size_t s)
{
if (!mem_map || !mem_map->root)
return NULL;
Thomas Schwinge
committed
return lookup_dev_1 (mem_map->root, (uintptr_t) d, s);
Thomas Schwinge
committed
/* OpenACC is silent on how memory exhaustion is indicated. We return
NULL. */
void *
acc_malloc (size_t s)
{
if (!s)
return NULL;
goacc_lazy_initialize ();
struct goacc_thread *thr = goacc_thread ();
Cesar Philippidis
committed
if (thr->dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
return malloc (s);
acc_prof_info prof_info;
acc_api_info api_info;
bool profiling_p = GOACC_PROFILING_SETUP_P (thr, &prof_info, &api_info);
void *res = thr->dev->alloc_func (thr->dev->target_id, s);
if (profiling_p)
{
thr->prof_info = NULL;
thr->api_info = NULL;
}
return res;
}
void
acc_free (void *d)
{
splay_tree_key k;
if (!d)
return;
Julian Brown
committed
struct goacc_thread *thr = goacc_thread ();
assert (thr && thr->dev);
Julian Brown
committed
struct gomp_device_descr *acc_dev = thr->dev;
Cesar Philippidis
committed
if (acc_dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
return free (d);
acc_prof_info prof_info;
acc_api_info api_info;
bool profiling_p = GOACC_PROFILING_SETUP_P (thr, &prof_info, &api_info);
Julian Brown
committed
gomp_mutex_lock (&acc_dev->lock);
/* We don't have to call lazy open here, as the ptr value must have
been returned by acc_malloc. It's not permitted to pass NULL in
(unless you got that null from acc_malloc). */
Thomas Schwinge
committed
if ((k = lookup_dev (&acc_dev->mem_map, d, 1)))
Julian Brown
committed
{
void *offset = d - k->tgt->tgt_start + k->tgt_offset;
void *h = k->host_start + offset;
size_t h_size = k->host_end - k->host_start;
Julian Brown
committed
gomp_mutex_unlock (&acc_dev->lock);
/* PR92503 "[OpenACC] Behavior of 'acc_free' if the memory space is still
used in a mapping". */
gomp_fatal ("refusing to free device memory space at %p that is still"
" mapped at [%p,+%d]",
d, h, (int) h_size);
Julian Brown
committed
}
else
gomp_mutex_unlock (&acc_dev->lock);
if (!acc_dev->free_func (acc_dev->target_id, d))
gomp_fatal ("error in freeing device memory in %s", __FUNCTION__);
if (profiling_p)
{
thr->prof_info = NULL;
thr->api_info = NULL;
}
static void
memcpy_tofrom_device (bool from, void *d, void *h, size_t s, int async,
const char *libfnname)
{
/* No need to call lazy open here, as the device pointer must have
been obtained from a routine that did that. */
struct goacc_thread *thr = goacc_thread ();
assert (thr && thr->dev);
Cesar Philippidis
committed
if (thr->dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
{
if (from)
memmove (h, d, s);
else
memmove (d, h, s);
Cesar Philippidis
committed
return;
}
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.async = async;
prof_info.async_queue = prof_info.async;
}
goacc_aq aq = get_goacc_asyncqueue (async);
if (from)
gomp_copy_dev2host (thr->dev, aq, h, d, s);
else
gomp_copy_host2dev (thr->dev, aq, d, h, s, false, /* TODO: cbuf? */ NULL);
if (profiling_p)
{
thr->prof_info = NULL;
thr->api_info = NULL;
}
}
void
acc_memcpy_to_device (void *d, void *h, size_t s)
memcpy_tofrom_device (false, d, h, s, acc_async_sync, __FUNCTION__);
}
void
acc_memcpy_to_device_async (void *d, void *h, size_t s, int async)
{
memcpy_tofrom_device (false, d, h, s, async, __FUNCTION__);
}
void
acc_memcpy_from_device (void *h, void *d, size_t s)
{
memcpy_tofrom_device (true, d, h, s, acc_async_sync, __FUNCTION__);
}
Cesar Philippidis
committed
void
acc_memcpy_from_device_async (void *h, void *d, size_t s, int async)
{
memcpy_tofrom_device (true, d, h, s, async, __FUNCTION__);
}
/* Return the device pointer that corresponds to host data H. Or NULL
if no mapping. */
void *
acc_deviceptr (void *h)
{
splay_tree_key n;
void *d;
void *offset;
goacc_lazy_initialize ();
struct goacc_thread *thr = goacc_thread ();
Julian Brown
committed
struct gomp_device_descr *dev = thr->dev;
Cesar Philippidis
committed
if (thr->dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
return h;
/* In the following, no OpenACC Profiling Interface events can possibly be
generated. */
Julian Brown
committed
gomp_mutex_lock (&dev->lock);
Julian Brown
committed
n = lookup_host (dev, h, 1);
if (!n)
Julian Brown
committed
{
gomp_mutex_unlock (&dev->lock);
return NULL;
}
offset = h - n->host_start;
d = n->tgt->tgt_start + n->tgt_offset + offset;
Julian Brown
committed
gomp_mutex_unlock (&dev->lock);
return d;
}
/* Return the host pointer that corresponds to device data D. Or NULL
if no mapping. */
void *
acc_hostptr (void *d)
{
splay_tree_key n;
void *h;
void *offset;
goacc_lazy_initialize ();
struct goacc_thread *thr = goacc_thread ();
Julian Brown
committed
struct gomp_device_descr *acc_dev = thr->dev;
Cesar Philippidis
committed
if (thr->dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
return d;
/* In the following, no OpenACC Profiling Interface events can possibly be
generated. */
Julian Brown
committed
gomp_mutex_lock (&acc_dev->lock);
Thomas Schwinge
committed
n = lookup_dev (&acc_dev->mem_map, d, 1);
if (!n)
Julian Brown
committed
{
gomp_mutex_unlock (&acc_dev->lock);
return NULL;
}
offset = d - n->tgt->tgt_start + n->tgt_offset;
h = n->host_start + offset;
Julian Brown
committed
gomp_mutex_unlock (&acc_dev->lock);
return h;
}
/* Return 1 if host data [H,+S] is present on the device. */
int
acc_is_present (void *h, size_t s)
{
splay_tree_key n;
if (!s || !h)
return 0;
goacc_lazy_initialize ();
struct goacc_thread *thr = goacc_thread ();
struct gomp_device_descr *acc_dev = thr->dev;
Cesar Philippidis
committed
if (thr->dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
return h != NULL;
/* In the following, no OpenACC Profiling Interface events can possibly be
generated. */
Julian Brown
committed
gomp_mutex_lock (&acc_dev->lock);
n = lookup_host (acc_dev, h, s);
if (n && ((uintptr_t)h < n->host_start
|| (uintptr_t)h + s > n->host_end
|| s > n->host_end - n->host_start))
n = NULL;
Julian Brown
committed
gomp_mutex_unlock (&acc_dev->lock);
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
return n != NULL;
}
/* Create a mapping for host [H,+S] -> device [D,+S] */
void
acc_map_data (void *h, void *d, size_t s)
{
size_t mapnum = 1;
void *hostaddrs = h;
void *devaddrs = d;
size_t sizes = s;
unsigned short kinds = GOMP_MAP_ALLOC;
goacc_lazy_initialize ();
struct goacc_thread *thr = goacc_thread ();
struct gomp_device_descr *acc_dev = thr->dev;
if (acc_dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
{
if (d != h)
gomp_fatal ("cannot map data on shared-memory system");
}
else
{
struct goacc_thread *thr = goacc_thread ();
if (!d || !h || !s)
gomp_fatal ("[%p,+%d]->[%p,+%d] is a bad map",
(void *)h, (int)s, (void *)d, (int)s);
acc_prof_info prof_info;
acc_api_info api_info;
bool profiling_p = GOACC_PROFILING_SETUP_P (thr, &prof_info, &api_info);
Julian Brown
committed
gomp_mutex_lock (&acc_dev->lock);
if (lookup_host (acc_dev, h, s))
Julian Brown
committed
{
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("host address [%p, +%d] is already mapped", (void *)h,
(int)s);
}
Thomas Schwinge
committed
if (lookup_dev (&thr->dev->mem_map, d, s))
Julian Brown
committed
{
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("device address [%p, +%d] is already mapped", (void *)d,
(int)s);
}
gomp_mutex_unlock (&acc_dev->lock);
struct target_mem_desc *tgt
= goacc_map_vars (acc_dev, NULL, mapnum, &hostaddrs, &devaddrs, &sizes,
&kinds, true, GOMP_MAP_VARS_ENTER_DATA);
Thomas Schwinge
committed
assert (tgt);
assert (tgt->list_count == 1);
Thomas Schwinge
committed
splay_tree_key n = tgt->list[0].key;
Thomas Schwinge
committed
assert (n->refcount == 1);
assert (n->dynamic_refcount == 0);
Thomas Schwinge
committed
/* Special reference counting behavior. */
n->refcount = REFCOUNT_INFINITY;
if (profiling_p)
{
thr->prof_info = NULL;
thr->api_info = NULL;
}
}
}
void
acc_unmap_data (void *h)
{
struct goacc_thread *thr = goacc_thread ();
struct gomp_device_descr *acc_dev = thr->dev;
/* No need to call lazy open, as the address must have been mapped. */
Cesar Philippidis
committed
/* This is a no-op on shared-memory targets. */
if (acc_dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
return;
acc_prof_info prof_info;
acc_api_info api_info;
bool profiling_p = GOACC_PROFILING_SETUP_P (thr, &prof_info, &api_info);
Julian Brown
committed
gomp_mutex_lock (&acc_dev->lock);
splay_tree_key n = lookup_host (acc_dev, h, 1);
if (!n)
Julian Brown
committed
{
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("%p is not a mapped block", (void *)h);
}
size_t host_size = n->host_end - n->host_start;
if (n->host_start != (uintptr_t) h)
Julian Brown
committed
{
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("[%p,%d] surrounds %p",
(void *) n->host_start, (int) host_size, (void *) h);
}
Thomas Schwinge
committed
/* TODO This currently doesn't catch 'REFCOUNT_INFINITY' usage different from
'acc_map_data'. Maybe 'dynamic_refcount' can be used for disambiguating
Thomas Schwinge
committed
the different 'REFCOUNT_INFINITY' cases, or simply separate
'REFCOUNT_INFINITY' values per different usage ('REFCOUNT_ACC_MAP_DATA'
etc.)? */
else if (n->refcount != REFCOUNT_INFINITY)
{
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("refusing to unmap block [%p,+%d] that has not been mapped"
" by 'acc_map_data'",
(void *) h, (int) host_size);
}
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("cannot unmap target block");
}
/* Above, we've verified that the mapping must have been set up by
'acc_map_data'. */
assert (tgt->refcount == 1);
/* Nullifying these fields prevents 'gomp_unmap_tgt' via 'gomp_remove_var'
from freeing the target memory. */
tgt->tgt_end = 0;
tgt->to_free = NULL;
bool is_tgt_unmapped = gomp_remove_var (acc_dev, n);
assert (is_tgt_unmapped);
Julian Brown
committed
Thomas Schwinge
committed
gomp_mutex_unlock (&acc_dev->lock);
if (profiling_p)
{
thr->prof_info = NULL;
thr->api_info = NULL;
}
/* Helper function to map a single dynamic data item, represented by a single
mapping. The acc_dev->lock should be held on entry, and remains locked on
exit. */
static void *
goacc_map_var_existing (struct gomp_device_descr *acc_dev, void *hostaddr,
size_t size, splay_tree_key n)
{
assert (n);
/* Present. */
void *d = (void *) (n->tgt->tgt_start + n->tgt_offset + hostaddr
- n->host_start);
if (hostaddr + size > (void *) n->host_end)
{
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("[%p,+%d] not mapped", hostaddr, (int) size);
}
assert (n->refcount != REFCOUNT_LINK);
if (n->refcount != REFCOUNT_INFINITY)
n->refcount++;
n->dynamic_refcount++;
return d;
}
/* Enter dynamic mapping for a single datum. Return the device pointer. */
static void *
goacc_enter_datum (void **hostaddrs, size_t *sizes, void *kinds, int async)
{
void *d;
splay_tree_key n;
gomp_fatal ("[%p,+%d] is a bad range", hostaddrs[0], (int) sizes[0]);
goacc_lazy_initialize ();
struct goacc_thread *thr = goacc_thread ();
struct gomp_device_descr *acc_dev = thr->dev;
Cesar Philippidis
committed
if (acc_dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
Cesar Philippidis
committed
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.async = async;
prof_info.async_queue = prof_info.async;
}
Julian Brown
committed
gomp_mutex_lock (&acc_dev->lock);
n = lookup_host (acc_dev, hostaddrs[0], sizes[0]);
d = goacc_map_var_existing (acc_dev, hostaddrs[0], sizes[0], n);
gomp_mutex_unlock (&acc_dev->lock);
}
Julian Brown
committed
gomp_mutex_unlock (&acc_dev->lock);
goacc_aq aq = get_goacc_asyncqueue (async);
struct target_mem_desc *tgt
= goacc_map_vars (acc_dev, aq, mapnum, hostaddrs, NULL, sizes,
kinds, true, GOMP_MAP_VARS_ENTER_DATA);
assert (tgt);
assert (tgt->list_count == 1);
n = tgt->list[0].key;
assert (n);
assert (n->refcount == 1);
assert (n->dynamic_refcount == 0);
n->dynamic_refcount++;
d = (void *) tgt->tgt_start;
if (profiling_p)
{
thr->prof_info = NULL;
thr->api_info = NULL;
}
return d;
}
void *
acc_create (void *h, size_t s)
{
unsigned short kinds[1] = { GOMP_MAP_ALLOC };
return goacc_enter_datum (&h, &s, &kinds, acc_async_sync);
}
void
acc_create_async (void *h, size_t s, int async)
{
unsigned short kinds[1] = { GOMP_MAP_ALLOC };
goacc_enter_datum (&h, &s, &kinds, async);
/* acc_present_or_create used to be what acc_create is now. */
/* acc_pcreate is acc_present_or_create by a different name. */
#ifdef HAVE_ATTRIBUTE_ALIAS
strong_alias (acc_create, acc_present_or_create)
strong_alias (acc_create, acc_pcreate)
#else
void *
acc_present_or_create (void *h, size_t s)
{
return acc_create (h, s);
void *
acc_pcreate (void *h, size_t s)
{
return acc_create (h, s);
acc_copyin (void *h, size_t s)
unsigned short kinds[1] = { GOMP_MAP_TO };
return goacc_enter_datum (&h, &s, &kinds, acc_async_sync);
void
acc_copyin_async (void *h, size_t s, int async)
{
unsigned short kinds[1] = { GOMP_MAP_TO };
goacc_enter_datum (&h, &s, &kinds, async);
}
/* acc_present_or_copyin used to be what acc_copyin is now. */
/* acc_pcopyin is acc_present_or_copyin by a different name. */
#ifdef HAVE_ATTRIBUTE_ALIAS
strong_alias (acc_copyin, acc_present_or_copyin)
strong_alias (acc_copyin, acc_pcopyin)
void *
acc_present_or_copyin (void *h, size_t s)
{
return acc_copyin (h, s);
}
void *
acc_pcopyin (void *h, size_t s)
{
return acc_copyin (h, s);
/* Helper function to unmap a single data item. Device lock should be held on
entry, and remains locked on exit. */
static void
goacc_exit_datum_1 (struct gomp_device_descr *acc_dev, void *h, size_t s,
unsigned short kind, splay_tree_key n, goacc_aq aq)
assert (kind != GOMP_MAP_DETACH
&& kind != GOMP_MAP_FORCE_DETACH);
if ((uintptr_t) h < n->host_start || (uintptr_t) h + s > n->host_end)
Julian Brown
committed
{
size_t host_size = n->host_end - n->host_start;
Julian Brown
committed
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("[%p,+%d] outside mapped block [%p,+%d]",
(void *) h, (int) s, (void *) n->host_start, (int) host_size);
Julian Brown
committed
}
bool finalize = (kind == GOMP_MAP_FORCE_FROM
|| kind == GOMP_MAP_DELETE);
assert (n->refcount != REFCOUNT_LINK);
if (n->refcount != REFCOUNT_INFINITY
&& n->refcount < n->dynamic_refcount)
{
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("Dynamic reference counting assert fail\n");
}
if (finalize)
Thomas Schwinge
committed
if (n->refcount != REFCOUNT_INFINITY)
n->refcount -= n->dynamic_refcount;
n->dynamic_refcount = 0;
else if (n->dynamic_refcount)
Thomas Schwinge
committed
if (n->refcount != REFCOUNT_INFINITY)
n->refcount--;
if (n->refcount == 0)
{
bool copyout = (kind == GOMP_MAP_FROM
|| kind == GOMP_MAP_FORCE_FROM);
if (copyout)
void *d = (void *) (n->tgt->tgt_start + n->tgt_offset
+ (uintptr_t) h - n->host_start);
gomp_copy_dev2host (acc_dev, aq, h, d, s);
Thomas Schwinge
committed
if (aq)
/* TODO We can't do the 'is_tgt_unmapped' checking -- see the
'gomp_unref_tgt' comment in
<http://mid.mail-archive.com/878snl36eu.fsf@euler.schwinge.homeip.net>;
PR92881. */
gomp_remove_var_async (acc_dev, n, aq);
else
{
size_t num_mappings = 0;
/* If the target_mem_desc represents a single data mapping, we can
check that it is freed when this splay tree key's refcount reaches
zero. Otherwise (e.g. for a 'GOMP_MAP_STRUCT' mapping with
multiple members), fall back to skipping the test. */
for (size_t l_i = 0; l_i < n->tgt->list_count; ++l_i)
if (n->tgt->list[l_i].key
&& !n->tgt->list[l_i].is_attach)
Thomas Schwinge
committed
bool is_tgt_unmapped = gomp_remove_var (acc_dev, n);
assert (is_tgt_unmapped || num_mappings > 1);
Thomas Schwinge
committed
}
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
}
/* Exit a dynamic mapping for a single variable. */
static void
goacc_exit_datum (void *h, size_t s, unsigned short kind, int async)
{
/* No need to call lazy open, as the data must already have been
mapped. */
kind &= 0xff;
struct goacc_thread *thr = goacc_thread ();
struct gomp_device_descr *acc_dev = thr->dev;
if (acc_dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
return;
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.async = async;
prof_info.async_queue = prof_info.async;
}
gomp_mutex_lock (&acc_dev->lock);
splay_tree_key n = lookup_host (acc_dev, h, s);
/* Non-present data is a no-op: PR92726, RP92970, PR92984. */
if (n)
{
goacc_aq aq = get_goacc_asyncqueue (async);
goacc_exit_datum_1 (acc_dev, h, s, kind, n, aq);
}
gomp_mutex_unlock (&acc_dev->lock);
if (profiling_p)
{
thr->prof_info = NULL;
thr->api_info = NULL;
}
}
void
acc_delete (void *h , size_t s)
{
goacc_exit_datum (h, s, GOMP_MAP_RELEASE, acc_async_sync);
}
void
acc_delete_async (void *h , size_t s, int async)
{
goacc_exit_datum (h, s, GOMP_MAP_RELEASE, async);
void
acc_delete_finalize (void *h , size_t s)
{
goacc_exit_datum (h, s, GOMP_MAP_DELETE, acc_async_sync);
}
void
acc_delete_finalize_async (void *h , size_t s, int async)
{
goacc_exit_datum (h, s, GOMP_MAP_DELETE, async);
void
acc_copyout (void *h, size_t s)
goacc_exit_datum (h, s, GOMP_MAP_FROM, acc_async_sync);
}
void
acc_copyout_async (void *h, size_t s, int async)
{
goacc_exit_datum (h, s, GOMP_MAP_FROM, async);
void
acc_copyout_finalize (void *h, size_t s)
{
goacc_exit_datum (h, s, GOMP_MAP_FORCE_FROM, acc_async_sync);
}
void
acc_copyout_finalize_async (void *h, size_t s, int async)
{
goacc_exit_datum (h, s, GOMP_MAP_FORCE_FROM, async);
static void
update_dev_host (int is_dev, void *h, size_t s, int async)
{
splay_tree_key n;
void *d;
Thomas Schwinge
committed
goacc_lazy_initialize ();
struct goacc_thread *thr = goacc_thread ();
struct gomp_device_descr *acc_dev = thr->dev;
Cesar Philippidis
committed
if (acc_dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
return;
/* Fortran optional arguments that are non-present result in a
NULL host address here. This can safely be ignored as it is
not possible to 'update' a non-present optional argument. */
if (h == NULL)
return;
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.async = async;
prof_info.async_queue = prof_info.async;
}
Julian Brown
committed
gomp_mutex_lock (&acc_dev->lock);
n = lookup_host (acc_dev, h, s);
if (!n)
Julian Brown
committed
{
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("[%p,%d] is not mapped", h, (int)s);
}
James Norris
committed
d = (void *) (n->tgt->tgt_start + n->tgt_offset
+ (uintptr_t) h - n->host_start);
goacc_aq aq = get_goacc_asyncqueue (async);
if (is_dev)
gomp_copy_host2dev (acc_dev, aq, d, h, s, false, /* TODO: cbuf? */ NULL);
gomp_copy_dev2host (acc_dev, aq, h, d, s);
gomp_mutex_unlock (&acc_dev->lock);
if (profiling_p)
{
thr->prof_info = NULL;
thr->api_info = NULL;
}
}
void
acc_update_device (void *h, size_t s)
{
update_dev_host (1, h, s, acc_async_sync);
}
void
acc_update_device_async (void *h, size_t s, int async)
{
update_dev_host (1, h, s, async);
}
void
acc_update_self (void *h, size_t s)
{
update_dev_host (0, h, s, acc_async_sync);
}
void
acc_update_self_async (void *h, size_t s, int async)
{
update_dev_host (0, h, s, async);
void
acc_attach_async (void **hostaddr, int async)
{
struct goacc_thread *thr = goacc_thread ();
struct gomp_device_descr *acc_dev = thr->dev;
goacc_aq aq = get_goacc_asyncqueue (async);
struct splay_tree_key_s cur_node;
splay_tree_key n;
if (thr->dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
return;
gomp_mutex_lock (&acc_dev->lock);
cur_node.host_start = (uintptr_t) hostaddr;
cur_node.host_end = cur_node.host_start + sizeof (void *);
n = splay_tree_lookup (&acc_dev->mem_map, &cur_node);
if (n == NULL)
{
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("struct not mapped for acc_attach");
}
gomp_attach_pointer (acc_dev, aq, &acc_dev->mem_map, n, (uintptr_t) hostaddr,
0, NULL, false);
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
gomp_mutex_unlock (&acc_dev->lock);
}
void
acc_attach (void **hostaddr)
{
acc_attach_async (hostaddr, acc_async_sync);
}
static void
goacc_detach_internal (void **hostaddr, int async, bool finalize)
{
struct goacc_thread *thr = goacc_thread ();
struct gomp_device_descr *acc_dev = thr->dev;
struct splay_tree_key_s cur_node;
splay_tree_key n;
struct goacc_asyncqueue *aq = get_goacc_asyncqueue (async);
if (thr->dev->capabilities & GOMP_OFFLOAD_CAP_SHARED_MEM)
return;
gomp_mutex_lock (&acc_dev->lock);
cur_node.host_start = (uintptr_t) hostaddr;
cur_node.host_end = cur_node.host_start + sizeof (void *);
n = splay_tree_lookup (&acc_dev->mem_map, &cur_node);
if (n == NULL)
{
gomp_mutex_unlock (&acc_dev->lock);
gomp_fatal ("struct not mapped for acc_detach");
}
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
gomp_detach_pointer (acc_dev, aq, n, (uintptr_t) hostaddr, finalize, NULL);
gomp_mutex_unlock (&acc_dev->lock);
}
void
acc_detach (void **hostaddr)
{
goacc_detach_internal (hostaddr, acc_async_sync, false);
}
void
acc_detach_async (void **hostaddr, int async)
{
goacc_detach_internal (hostaddr, async, false);
}
void
acc_detach_finalize (void **hostaddr)
{
goacc_detach_internal (hostaddr, acc_async_sync, true);
}
void
acc_detach_finalize_async (void **hostaddr, int async)
{