From: Julian Brown Date: Fri, 30 Nov 2018 10:40:49 +0000 (-0800) Subject: OpenACC reference count consistency checking X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6701effc000a9b37b26eaaa6667a41b92c2e84db;p=thirdparty%2Fgcc.git OpenACC reference count consistency checking 2018-11-28 Julian Brown libgomp/ * libgomp.h (RC_CHECKING): New macro, disabled by default, guarding all hunks in this patch. (target_mem_desc): Add forward declaration. (async_tgt_use): New struct. (target_mem_desc): Add refcount_chk, mark fields. (acc_dispatch_t): Add tgt_uses, au_lock fields. (dump_tgt, gomp_rc_check): Add prototypes. * oacc-async (goacc_async_unmap_tgt): Add refcount self-check code. (goacc_async_copyout_unmap_vars): Likewise. (goacc_remove_var_async): Likewise. * oacc-parallel.c (GOACC_parallel_keyed_internal): Add refcount self-check code. (GOACC_data_start, GOACC_data_end, GOACC_enter_exit_data): Likewise. * target.c (stdio.h): Include. (dump_tgt, rc_check_clear, rc_check_count, rc_check_verify) (gomp_rc_check): New functions to consistency-check reference counts. (gomp_target_init): Initialise self-check-related device fields. (cherry picked from openacc-gcc-9-branch commit ee87cb3436ca56c7ebe96768f7d13e3d959fbbd5) --- diff --git a/libgomp/ChangeLog.omp b/libgomp/ChangeLog.omp index 58010c60285e..3c0bdf9a0992 100644 --- a/libgomp/ChangeLog.omp +++ b/libgomp/ChangeLog.omp @@ -1,3 +1,23 @@ +2018-11-28 Julian Brown + + * libgomp.h (RC_CHECKING): New macro, disabled by default, guarding all + hunks in this patch. + (target_mem_desc): Add forward declaration. + (async_tgt_use): New struct. + (target_mem_desc): Add refcount_chk, mark fields. + (acc_dispatch_t): Add tgt_uses, au_lock fields. + (dump_tgt, gomp_rc_check): Add prototypes. + * oacc-async (goacc_async_unmap_tgt): Add refcount self-check code. + (goacc_async_copyout_unmap_vars): Likewise. + (goacc_remove_var_async): Likewise. + * oacc-parallel.c (GOACC_parallel_keyed_internal): Add refcount + self-check code. + (GOACC_data_start, GOACC_data_end, GOACC_enter_exit_data): Likewise. + * target.c (stdio.h): Include. + (dump_tgt, rc_check_clear, rc_check_count, rc_check_verify) + (gomp_rc_check): New functions to consistency-check reference counts. + (gomp_target_init): Initialise self-check-related device fields. + 2018-12-14 Julian Brown * libgomp.h (struct target_var_desc): Add do_detach flag. diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h index d8262951fa70..cf3682a09eee 100644 --- a/libgomp/libgomp.h +++ b/libgomp/libgomp.h @@ -875,9 +875,17 @@ struct target_var_desc { uintptr_t length; }; +/* Uncomment to enable reference-count consistency checking (for development + use only). */ +//#define RC_CHECKING 1 + struct target_mem_desc { /* Reference count. */ uintptr_t refcount; +#ifdef RC_CHECKING + uintptr_t refcount_chk; + bool mark; +#endif /* All the splay nodes allocated together. */ splay_tree_node array; /* Start of the target region. */ @@ -932,6 +940,10 @@ struct splay_tree_key_s { If set to VREFCOUNT_LINK_KEY (for OpenMP, where this field is not otherwise needed), the union below represents a link key. */ uintptr_t virtual_refcount; +#ifdef RC_CHECKING + /* The recalculated reference count, for verification. */ + uintptr_t refcount_chk; +#endif union { /* For a block with attached pointers, the attachment counters for each. Only used for OpenACC. */ @@ -1097,6 +1109,12 @@ extern void gomp_detach_pointer (struct gomp_device_descr *, struct goacc_asyncqueue *, splay_tree_key, uintptr_t, bool, struct gomp_coalesce_buf *); +#ifdef RC_CHECKING +extern void dump_tgt (const char *, struct target_mem_desc *); +extern void gomp_rc_check (struct gomp_device_descr *, + struct target_mem_desc *); +#endif + extern struct target_mem_desc *gomp_map_vars (struct gomp_device_descr *, size_t, void **, void **, size_t *, void *, bool, diff --git a/libgomp/oacc-parallel.c b/libgomp/oacc-parallel.c index bd810e3d0862..f588d7e42b5a 100644 --- a/libgomp/oacc-parallel.c +++ b/libgomp/oacc-parallel.c @@ -257,7 +257,16 @@ GOACC_parallel_keyed (int flags_m, void (*fn) (void *), tgt = gomp_map_vars_async (acc_dev, aq, mapnum, hostaddrs, NULL, sizes, kinds, true, GOMP_MAP_VARS_OPENACC); - + +#ifdef RC_CHECKING + gomp_mutex_lock (&acc_dev->lock); + assert (tgt); + dump_tgt (__FUNCTION__, tgt); + tgt->prev = thr->mapped_data; + gomp_rc_check (acc_dev, tgt); + gomp_mutex_unlock (&acc_dev->lock); +#endif + devaddrs = gomp_alloca (sizeof (void *) * mapnum); for (i = 0; i < mapnum; i++) devaddrs[i] = (void *) gomp_map_val (tgt, hostaddrs, i); @@ -275,6 +284,12 @@ GOACC_parallel_keyed (int flags_m, void (*fn) (void *), dims, tgt, aq); gomp_unmap_vars_async (tgt, true, aq); } + +#ifdef RC_CHECKING + gomp_mutex_lock (&acc_dev->lock); + gomp_rc_check (acc_dev, thr->mapped_data); + gomp_mutex_unlock (&acc_dev->lock); +#endif } /* Legacy entry point, only provide host execution. */ @@ -330,6 +345,12 @@ GOACC_data_start (int flags_m, size_t mapnum, gomp_debug (0, " %s: mappings prepared\n", __FUNCTION__); tgt->prev = thr->mapped_data; thr->mapped_data = tgt; + +#ifdef RC_CHECKING + gomp_mutex_lock (&acc_dev->lock); + gomp_rc_check (acc_dev, thr->mapped_data); + gomp_mutex_unlock (&acc_dev->lock); +#endif } void @@ -342,6 +363,12 @@ GOACC_data_end (void) thr->mapped_data = tgt->prev; gomp_unmap_vars (tgt, true); gomp_debug (0, " %s: mappings restored\n", __FUNCTION__); + +#ifdef RC_CHECKING + gomp_mutex_lock (&thr->dev->lock); + gomp_rc_check (thr->dev, thr->mapped_data); + gomp_mutex_unlock (&thr->dev->lock); +#endif } void @@ -622,6 +649,12 @@ GOACC_enter_exit_data (int flags_m, size_t mapnum, } } } + +#ifdef RC_CHECKING + gomp_mutex_lock (&acc_dev->lock); + gomp_rc_check (acc_dev, thr->mapped_data); + gomp_mutex_unlock (&acc_dev->lock); +#endif } static void diff --git a/libgomp/target.c b/libgomp/target.c index 7e3889db0182..3cac880efa26 100644 --- a/libgomp/target.c +++ b/libgomp/target.c @@ -39,6 +39,9 @@ #include #include #include +#ifdef RC_CHECKING +#include +#endif #ifdef PLUGIN_SUPPORT #include @@ -359,6 +362,185 @@ gomp_free_device_memory (struct gomp_device_descr *devicep, void *devptr) } } +#ifdef RC_CHECKING +void +dump_tgt (const char *where, struct target_mem_desc *tgt) +{ + if (!getenv ("GOMP_DEBUG_TGT")) + return; + + fprintf (stderr, "%s: %s: tgt=%p\n", __FUNCTION__, where, (void*) tgt); + fprintf (stderr, "refcount=%d\n", (int) tgt->refcount); + fprintf (stderr, "tgt_start=%p\n", (void*) tgt->tgt_start); + fprintf (stderr, "tgt_end=%p\n", (void*) tgt->tgt_end); + fprintf (stderr, "to_free=%p\n", tgt->to_free); + fprintf (stderr, "list_count=%d\n", (int) tgt->list_count); + for (int i = 0; i < tgt->list_count; i++) + { + fprintf (stderr, "list item %d:\n", i); + fprintf (stderr, " key: %p\n", (void*) tgt->list[i].key); + if (tgt->list[i].key) + { + fprintf (stderr, " key.host_start=%p\n", + (void*) tgt->list[i].key->host_start); + fprintf (stderr, " key.host_end=%p\n", + (void*) tgt->list[i].key->host_end); + fprintf (stderr, " key.tgt=%p\n", (void*) tgt->list[i].key->tgt); + fprintf (stderr, " key.offset=%d\n", + (int) tgt->list[i].key->tgt_offset); + fprintf (stderr, " key.refcount=%d\n", + (int) tgt->list[i].key->refcount); + if (tgt->list[i].key->virtual_refcount == VREFCOUNT_LINK_KEY) + fprintf (stderr, " key.u.link_key=%p\n", + (void*) tgt->list[i].key->u.link_key); + else + { + fprintf (stderr, " key.virtual_refcount=%d\n", + (int) tgt->list[i].key->virtual_refcount); + fprintf (stderr, " key.u.attach_count=%p\n", + (void*) tgt->list[i].key->u.attach_count); + } + } + } + fprintf (stderr, "\n"); +} + +static void +rc_check_clear (splay_tree_node node) +{ + splay_tree_key k = &node->key; + + k->refcount_chk = 0; + k->tgt->refcount_chk = 0; + k->tgt->mark = false; + + if (node->left) + rc_check_clear (node->left); + if (node->right) + rc_check_clear (node->right); +} + +static void +rc_check_count (splay_tree_node node) +{ + splay_tree_key k = &node->key; + struct target_mem_desc *t; + + /* Add virtual reference counts ("acc enter data", etc.) for this key. */ + k->refcount_chk += k->virtual_refcount; + + t = k->tgt; + t->refcount_chk++; + + if (!t->mark) + { + for (int i = 0; i < t->list_count; i++) + if (t->list[i].key) + t->list[i].key->refcount_chk++; + + t->mark = true; + } + + if (node->left) + rc_check_count (node->left); + if (node->right) + rc_check_count (node->right); +} + +static bool +rc_check_verify (splay_tree_node node, bool noisy, bool errors) +{ + splay_tree_key k = &node->key; + struct target_mem_desc *t; + + if (k->refcount != REFCOUNT_INFINITY) + { + if (noisy) + fprintf (stderr, "key %p (%p..+%d): rc=%d/%d, virt_rc=%d\n", k, + (void *) k->host_start, (int) (k->host_end - k->host_start), + (int) k->refcount, (int) k->refcount_chk, + (int) k->virtual_refcount); + + if (k->refcount != k->refcount_chk) + { + if (noisy) + fprintf (stderr, " -- key refcount mismatch!\n"); + errors = true; + } + + t = k->tgt; + + if (noisy) + fprintf (stderr, "tgt %p: rc=%d/%d\n", t, (int) t->refcount, + (int) t->refcount_chk); + + if (t->refcount != t->refcount_chk) + { + if (noisy) + fprintf (stderr, + " -- target memory descriptor refcount mismatch!\n"); + errors = true; + } + } + + if (node->left) + errors |= rc_check_verify (node->left, noisy, errors); + if (node->right) + errors |= rc_check_verify (node->right, noisy, errors); + + return errors; +} + +/* Call with device locked. */ + +attribute_hidden void +gomp_rc_check (struct gomp_device_descr *devicep, struct target_mem_desc *tgt) +{ + splay_tree sp = &devicep->mem_map; + + bool noisy = getenv ("GOMP_DEBUG_TGT") != 0; + + if (noisy) + fprintf (stderr, "\n*** GOMP_RC_CHECK ***\n\n"); + + if (sp->root) + { + rc_check_clear (sp->root); + + for (struct target_mem_desc *t = tgt; t; t = t->prev) + { + t->refcount_chk = 0; + t->mark = false; + } + + /* Add references for interconnected splay-tree keys. */ + rc_check_count (sp->root); + + /* Add references for the tgt for a currently-executing kernel and/or + any enclosing data directives. */ + for (struct target_mem_desc *t = tgt; t; t = t->prev) + { + t->refcount_chk++; + + if (!t->mark) + { + for (int i = 0; i < t->list_count; i++) + if (t->list[i].key) + t->list[i].key->refcount_chk++; + + t->mark = true; + } + } + + if (rc_check_verify (sp->root, noisy, false)) + { + gomp_mutex_unlock (&devicep->lock); + gomp_fatal ("refcount checking failure"); + } + } +} +#endif + /* Handle the case where gomp_map_lookup, splay_tree_lookup or gomp_map_0len_lookup found oldn for newn. Helper function of gomp_map_vars. */