[gcc/devel/omp/gcc-16] OpenMP: Add omp_get_device_distances routine [PR125877]

Sandra Loosemore via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:7a5904c8e3717fb1014535ec3440209b4d30d1fa

commit 7a5904c8e3717fb1014535ec3440209b4d30d1fa
Author: Tobias Burnus <[email protected]>
Date:   Fri Jun 19 18:00:37 2026 +0200

    OpenMP: Add omp_get_device_distances routine [PR125877]
    
    On multi-device, multi-socket systems, this routine permits to
    find the GPU device that is closest to the thread calling this
    API routine.
    
            PR libgomp/125877
    
    include/ChangeLog:
    
            * cuda/cuda.h (cuDeviceGetPCIBusId): Declare.
    
    libgomp/ChangeLog:
    
            * affinity.c (gomp_get_current_numa_node,
            gomp_get_numa_distance): New functions.
            * config/linux/affinity.c (gomp_get_current_numa_node,
            gomp_get_numa_distance): Likewise.
            * libgomp-plugin.h (GOMP_OFFLOAD_get_numa_node): Declare.
            * libgomp.h (gomp_get_current_numa_node,
            gomp_get_numa_distance): Declare.
            (struct gomp_device_descr): Add get_numa_node_func.
            * libgomp.map (OMP_6.1): Add.
            * libgomp.texi (omp_get_device_distances): Add routine.
            * omp.h.in (omp_get_device_distances): Declare.
            * omp_lib.f90.in (omp_get_device_distances): Add interface.
            * omp_lib.h.in (omp_get_device_distances): Likewise.
            * plugin/cuda-lib.def (cuDeviceGetPCIBusId): Add.
            * plugin/plugin-gcn.c (struct agent_info): Add numa_node.
            (GOMP_OFFLOAD_get_numa_node): New.
            * plugin/plugin-nvptx.c (struct ptx_device): Add numa_node.
            (GOMP_OFFLOAD_get_numa_node): New.
            * target.c (omp_get_device_distances): New function.
            (gomp_load_plugin_for_device): Load get_numa_node symbol.
            * testsuite/libgomp.c/omp-get-device-distances.c: New test.
            * testsuite/libgomp.fortran/omp-get-device-distances.f90: New test.
    
    (cherry picked from commit 8af1592882509fbead16e30fd2d056330a9609e4)

Diff:
---
 include/cuda/cuda.h                                |   1 +
 libgomp/affinity.c                                 |  14 +++
 libgomp/config/linux/affinity.c                    |  68 ++++++++++++++
 libgomp/libgomp-plugin.h                           |   2 +
 libgomp/libgomp.h                                  |   3 +
 libgomp/libgomp.map                                |   5 +
 libgomp/libgomp.texi                               |  50 ++++++++++
 libgomp/omp.h.in                                   |   1 +
 libgomp/omp_lib.f90.in                             |  12 +++
 libgomp/omp_lib.h.in                               |  13 +++
 libgomp/plugin/cuda-lib.def                        |   1 +
 libgomp/plugin/plugin-gcn.c                        |  50 +++++++++-
 libgomp/plugin/plugin-nvptx.c                      |  43 +++++++++
 libgomp/target.c                                   |  53 +++++++++++
 .../testsuite/libgomp.c/omp-get-device-distances.c | 101 +++++++++++++++++++++
 .../libgomp.fortran/omp-get-device-distances.f90   |  53 +++++++++++
 16 files changed, 468 insertions(+), 2 deletions(-)

diff --git a/include/cuda/cuda.h b/include/cuda/cuda.h
index e9b574ed83a8..3397f0f5cec2 100644
--- a/include/cuda/cuda.h
+++ b/include/cuda/cuda.h
@@ -237,6 +237,7 @@ CUresult cuDeviceTotalMem (size_t *, CUdevice);
 CUresult cuDeviceGetAttribute (int *, CUdevice_attribute, CUdevice);
 CUresult cuDeviceGetCount (int *);
 CUresult cuDeviceGetName (char *, int, CUdevice);
+CUresult cuDeviceGetPCIBusId (char *, int, CUdevice);
 CUresult cuEventCreate (CUevent *, unsigned);
 #define cuEventDestroy cuEventDestroy_v2
 CUresult cuEventDestroy (CUevent);
diff --git a/libgomp/affinity.c b/libgomp/affinity.c
index a194ba97ba35..060fefbe540a 100644
--- a/libgomp/affinity.c
+++ b/libgomp/affinity.c
@@ -152,5 +152,19 @@ gomp_display_affinity_place (char *buffer, size_t size, size_t *ret,
   gomp_display_string (buffer, size, ret, buf, strlen (buf));
 }
 
+int
+gomp_get_current_numa_node ()
+{
+  return -1;
+}
+
+int
+gomp_get_numa_distance (int node1, int node2)
+{
+  (void) node1;
+  (void) node2;
+  return -1;
+}
+
 ialias(omp_get_place_num_procs)
 ialias(omp_get_place_proc_ids)
diff --git a/libgomp/config/linux/affinity.c b/libgomp/config/linux/affinity.c
index d30187b60643..ca14be9690e9 100644
--- a/libgomp/config/linux/affinity.c
+++ b/libgomp/config/linux/affinity.c
@@ -30,12 +30,14 @@
 #endif
 #include "libgomp.h"
 #include "proc.h"
+#include <dirent.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <limits.h>
+#include <sys/syscall.h>
 
 #ifdef HAVE_PTHREAD_AFFINITY_NP
 
@@ -624,3 +626,69 @@ ialias(omp_get_place_proc_ids)
 #include "../../affinity.c"
 
 #endif
+
+static int num_numa_nodes = 0;
+static int *numa_distances = NULL;
+
+int
+gomp_get_current_numa_node ()
+{
+  int node;
+  syscall (SYS_getcpu, NULL /* cpu */, &node, NULL /* no longer used */);
+  return node;
+}
+
+int
+gomp_get_numa_distance (int node1, int node2)
+{
+  if (node1 < 0 || node2 < 0 || num_numa_nodes < 0)
+    return -1;
+
+  if (numa_distances == NULL)
+    {
+      num_numa_nodes = -1;
+      DIR *dir = opendir ("/sys/devices/system/node");
+      if (!dir)
+        return -1;
+      struct dirent *dp;
+      int cnt = 0;
+      errno = 0;
+      while ((dp = readdir(dir)) != NULL)
+	if (strncmp ("node", dp->d_name, 4 /* strlen ("node") */) == 0)
+	  cnt++;
+	else if (errno)
+	  {
+	    closedir (dir);
+	    return -1;
+	  }
+      closedir (dir);
+      numa_distances = (int *) gomp_malloc (sizeof (int) * cnt * cnt);
+
+      constexpr int len = sizeof ("/sys/devices/system/node/node12345/"
+                                  "distance");
+      char filename[len];
+
+      for (int i = 0; i < cnt; i++)
+	{
+	  if (len < snprintf (filename, sizeof (filename),
+			      "/sys/devices/system/node/node%d/distance", i))
+	    return -1;
+	  int distance = -1;
+	  FILE *in = fopen (filename, "r");
+	  for (int j = 0; j < cnt; j++)
+	    {
+	      fscanf (in, "%d", &distance);
+	      if (distance == -1)
+		{
+		  fclose (in);
+		  free (numa_distances);
+		  return -1;
+		}
+	      numa_distances[i * cnt + j] = distance;
+	    }
+	  fclose (in);
+	}
+      num_numa_nodes = cnt;
+    }
+  return numa_distances[node1 * num_numa_nodes + node2];
+}
diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h
index 39c322255fd6..88841ac5817b 100644
--- a/libgomp/libgomp-plugin.h
+++ b/libgomp/libgomp-plugin.h
@@ -159,6 +159,8 @@ extern void GOMP_PLUGIN_target_rev (uint64_t, uint64_t, uint64_t, uint64_t,
 /* Prototypes for functions implemented by libgomp plugins.  */
 extern const char *GOMP_OFFLOAD_get_name (void);
 extern const char *GOMP_OFFLOAD_get_uid (int);
+extern const int GOMP_OFFLOAD_get_numa_node (int);
+
 extern unsigned int GOMP_OFFLOAD_get_caps (void);
 extern int GOMP_OFFLOAD_get_type (void);
 extern int GOMP_OFFLOAD_get_num_devices (unsigned int);
diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
index 1c45bda88252..25ff5c5c7815 100644
--- a/libgomp/libgomp.h
+++ b/libgomp/libgomp.h
@@ -613,6 +613,8 @@ extern int gomp_debug_var;
 extern bool gomp_display_affinity_var;
 extern char *gomp_affinity_format_var;
 extern size_t gomp_affinity_format_len;
+extern int gomp_get_current_numa_node ();
+extern int gomp_get_numa_distance (int, int);
 extern uintptr_t gomp_def_allocator;
 extern const struct gomp_default_icv gomp_default_icv_values;
 extern struct gomp_icv_list *gomp_initial_icv_list;
@@ -1446,6 +1448,7 @@ struct gomp_device_descr
   /* Function handlers.  */
   __typeof (GOMP_OFFLOAD_get_name) *get_name_func;
   __typeof (GOMP_OFFLOAD_get_uid) *get_uid_func;
+  __typeof (GOMP_OFFLOAD_get_numa_node) *get_numa_node_func;
   __typeof (GOMP_OFFLOAD_get_caps) *get_caps_func;
   __typeof (GOMP_OFFLOAD_get_type) *get_type_func;
   __typeof (GOMP_OFFLOAD_get_num_devices) *get_num_devices_func;
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 06db9040e966..7e8bb7ebec3b 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -247,6 +247,11 @@ OMP_6.0 {
 	omp_control_tool_8_;
 } OMP_5.2;
 
+OMP_6.1 {
+  global:
+	omp_get_device_distances;
+} OMP_6.0;
+
 GOMP_1.0 {
   global:
 	GOMP_atomic_end;
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index 7bc313a6fabf..cdf7d1e54b60 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -1740,6 +1740,7 @@ They have C linkage and do not throw exceptions.
 @c * omp_set_device_num_teams::  <fixme>/TR13
 @c * omp_get_device_teams_thread_limit::  <fixme>/TR13
 @c * omp_set_device_teams_thread_limit::  <fixme>/TR13
+* omp_get_device_distances:: Obtain the NUMA distance of a device
 @end menu
 
 
@@ -2018,6 +2019,55 @@ to @code{omp_initial_device}; and otherwise it is replaced with a call to
 @end table
 
 
+@node omp_get_device_distances
+@subsection @code{omp_get_device_distances} -- Obtain the NUMA distance of a device
+@table @asis
+@item @emph{Description}:
+The routine takes two arrays, @var{devs} and @var{distances}, that have at least
+the size @var{ndevs}.  For each element of @var{devs}, the corresponding element
+of @var{distances} is set to an implementation-defined non-negative distance
+between the place on which the routine was invoked and the device number
+specified in @var{devs}. For the host device, the distance is zero.
+
+The routine must be executed on the host device and all values of @var{devs}
+must be conforming devices numbers other than @code{omp_invalid_device}.
+
+In GCC the distance is the value returned by the operating system for the
+NUMA distance between the node on which the routine was executed and the node
+to which the device is attached to. There are two exceptions: As required by
+the specification, for the host device @code{0} is returned.  Additionally,
+when either the operating system reports a negative number or when GCC runtime
+cannot obtain the distance, the fixed value @code{10} is returned.  The former
+is in particular the case when the system is virtualized.  The values the
+operating system returns are usually based on the SLIT (System Locality Distance
+Information Table) of ACPI (Advanced Configuration and Power Interface) and have
+values between @code{10} and @code{254}.  If an invalid device number is
+specified, GCC returns @code{-1}.
+
+Fortran note: In GCC, this routine does not support passing default-kind integer
+arguments with @code{-fdefault-integer-8}.
+
+@item @emph{C/C++}
+@multitable @columnfractions .20 .80
+@item @emph{Prototype}: @tab @code{void omp_get_device_distances (int ndevs, const int *devs, int *distances)}
+@end multitable
+
+@item @emph{Fortran}:
+@multitable @columnfractions .20 .80
+@item @emph{Interface}: @tab @code{subroutine omp_get_device_distances (ndevs, devs, distances)}
+@item                   @tab @code{integer, intent(in) :: ndevs, devs(*)}
+@item                   @tab @code{integer, intent(out) :: distances(*)}
+@end multitable
+
+@item @emph{See also}:
+@ref{omp_get_num_devices}
+
+@item @emph{Reference}:
+@c TODO: Update once OpenMP 6.1 is released
+@uref{https://www.openmp.org, OpenMP specification Technical Report 15}, Section 3.16
+@end table
+
+
 
 @node Device Memory Routines
 @section Device Memory Routines
diff --git a/libgomp/omp.h.in b/libgomp/omp.h.in
index 4a4c3e97b534..cfb104dccbdd 100644
--- a/libgomp/omp.h.in
+++ b/libgomp/omp.h.in
@@ -449,6 +449,7 @@ extern const char *omp_get_interop_rc_desc (const omp_interop_t,
 					    omp_interop_rc_t) __GOMP_NOTHROW;
 
 extern int omp_get_device_from_uid (const char *) __GOMP_NOTHROW;
+extern void omp_get_device_distances(int, const int *, int *) __GOMP_NOTHROW;
 extern const char *omp_get_uid_from_device (int) __GOMP_NOTHROW;
 
 extern omp_control_tool_result_t omp_control_tool (omp_control_tool_t, int,
diff --git a/libgomp/omp_lib.f90.in b/libgomp/omp_lib.f90.in
index 015c59b1c81b..fc58b90f2d1f 100644
--- a/libgomp/omp_lib.f90.in
+++ b/libgomp/omp_lib.f90.in
@@ -1071,6 +1071,18 @@
           end function omp_get_uid_from_device_8
         end interface omp_get_uid_from_device
 
+        interface
+          ! This function only supports integer(4) for simplicity.
+          ! Contrary to OpenMP 6.1, it also uses bind(C) and value
+          ! and an explicit-size instead of assumed-size array.
+          subroutine omp_get_device_distances (ndevs, devs, distances) bind(C)
+            use iso_c_binding
+            integer(c_int), value, intent(in) :: ndevs
+            integer(c_int), intent(in) :: devs(ndevs)
+            integer(c_int), intent(out) :: distances(ndevs)
+          end subroutine omp_get_device_distances
+        end interface
+
         interface omp_control_tool
           integer (kind=omp_control_tool_result_kind) function &
               omp_control_tool (command, modifier)
diff --git a/libgomp/omp_lib.h.in b/libgomp/omp_lib.h.in
index ff9462fca9d0..d5e39e6c770d 100644
--- a/libgomp/omp_lib.h.in
+++ b/libgomp/omp_lib.h.in
@@ -687,6 +687,19 @@
         end function omp_get_uid_from_device_8
       end interface omp_get_uid_from_device
 
+      interface
+!       This function only supports integer(4) for simplicity.
+!       Contrary to OpenMP 6.1, it also uses bind(C) and value
+!       and an explicit-size instead of assumed-size array.
+        subroutine omp_get_device_distances (ndevs, devs, distances)            &
+     &      bind(C)
+          use iso_c_binding
+          integer(c_int), value, intent(in) :: ndevs
+          integer(c_int), intent(in) :: devs(ndevs)
+          integer(c_int), intent(out) :: distances(ndevs)
+        end subroutine omp_get_device_distances
+      end interface
+
       interface omp_control_tool
         integer (kind=omp_control_tool_result_kind) function                    &
      &      omp_control_tool (command, modifier)
diff --git a/libgomp/plugin/cuda-lib.def b/libgomp/plugin/cuda-lib.def
index f87987db85c7..fc96642c5aae 100644
--- a/libgomp/plugin/cuda-lib.def
+++ b/libgomp/plugin/cuda-lib.def
@@ -10,6 +10,7 @@ CUDA_ONE_CALL (cuDeviceGet)
 CUDA_ONE_CALL (cuDeviceGetAttribute)
 CUDA_ONE_CALL (cuDeviceGetCount)
 CUDA_ONE_CALL (cuDeviceGetName)
+CUDA_ONE_CALL_MAYBE_NULL (cuDeviceGetPCIBusId)
 CUDA_ONE_CALL_MAYBE_NULL (cuDeviceGetUuid)
 CUDA_ONE_CALL_MAYBE_NULL (cuDeviceGetUuid_v2)
 CUDA_ONE_CALL (cuDeviceTotalMem)
diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
index d4061e4b781e..8f5dc75734e8 100644
--- a/libgomp/plugin/plugin-gcn.c
+++ b/libgomp/plugin/plugin-gcn.c
@@ -503,11 +503,14 @@ struct agent_info
      thread should have locked agent->module_rwlock for reading before
      acquiring it.  */
   pthread_mutex_t prog_mutex;
+  /* HSA executable - the finalized program that is used to locate kernels.  */
+  hsa_executable_t executable;
+  /* NUMA node; 0 = not initialized, < 0 N/A (error or virtualized machine);
+     if > 0: actual node is 'numa_node - 1' (range: 0...(num-nodes -1)).  */
+  int numa_node;
   /* Flag whether the HSA program that consists of all the modules has been
      finalized.  */
   bool prog_finalized;
-  /* HSA executable - the finalized program that is used to locate kernels.  */
-  hsa_executable_t executable;
 };
 
 /* Information required to identify, finalize and run any given kernel.  */
@@ -3727,6 +3730,49 @@ GOMP_OFFLOAD_get_uid (int ord)
   return str;
 }
 
+/* Return the NUMA node of the GPU identified by ORD; returns -1 when
+   an error occurred; this value might also be returned if on
+   virtualized systems.
+   The implementation assumes that the Linux /sys is available.  */
+
+int
+GOMP_OFFLOAD_get_numa_node (int ord)
+{
+  hsa_status_t status, status2;
+  uint32_t domain, bdfid;
+  struct agent_info *agent = get_agent_info (ord);
+
+  /* Initialized to 0; to distinguish, save with offset.  */
+  if (agent->numa_node != 0)
+    return agent->numa_node > 0 ? agent->numa_node - 1 : agent->numa_node;
+
+  agent->numa_node = -1;
+
+  status = hsa_fns.hsa_agent_get_info_fn (agent->id, HSA_AMD_AGENT_INFO_DOMAIN,
+					  &domain);
+  status2 = hsa_fns.hsa_agent_get_info_fn (agent->id, HSA_AMD_AGENT_INFO_BDFID,
+					   &bdfid);
+  if (status != HSA_STATUS_SUCCESS || status2 != HSA_STATUS_SUCCESS)
+    return -1;
+
+  constexpr int len = sizeof ("/sys/bus/pci/devices/0000:00:00.00/numa_node");
+  char filename[len];
+  if (len < snprintf (filename, sizeof (filename),
+		      "/sys/bus/pci/devices/%04x:%02x:%02x.0/numa_node",
+		      domain, bdfid >> 8, bdfid & 0xFF))
+    return -1;
+
+  FILE *in = fopen (filename, "r");
+  if (!in)
+    return -1;
+  int numa_node = -1;
+  fscanf (in, "%d", &numa_node);
+  fclose (in);
+
+  agent->numa_node = numa_node >= 0 ? numa_node + 1 : numa_node;
+  return numa_node;
+}
+
 /* Return the specific capabilities the HSA accelerator have.  */
 
 unsigned int
diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index eb22651469db..4818ccb28844 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -328,6 +328,7 @@ struct ptx_device
   int warp_size;
   int max_threads_per_block;
   int max_threads_per_multiprocessor;
+  int numa_node;
   int default_dims[GOMP_DIM_MAX];
 
   /* Length as used by the CUDA Runtime API ('struct cudaDeviceProp').  */
@@ -1340,6 +1341,48 @@ GOMP_OFFLOAD_get_uid (int ord)
   return str;
 }
 
+/* Return the NUMA node of the GPU identified by ORD; returns -1 when
+   an error occurred; this value might also be returned if on
+   virtualized systems.
+   The implementation assumes that the Linux /sys is available.  */
+
+int
+GOMP_OFFLOAD_get_numa_node (int ord)
+{
+  CUresult r = CUDA_ERROR_NOT_FOUND;
+  char bus_id[14] = {};
+  struct ptx_device *dev = ptx_devices[ord];
+
+  /* Initialized to 0; to distinguish, save with offset.  */
+  if (dev->numa_node != 0)
+    return dev->numa_node > 0 ? dev->numa_node - 1 : dev->numa_node;
+
+  dev->numa_node = -1;
+
+  if (CUDA_CALL_EXISTS (cuDeviceGetPCIBusId))
+    r = CUDA_CALL_NOCHECK (cuDeviceGetPCIBusId, bus_id, sizeof (bus_id)-1,
+			   dev->dev);
+  if (bus_id[0] == '\0' || r != CUDA_SUCCESS)
+    return -1;
+
+  constexpr int len = (sizeof("/sys/bus/pci/devices//numa_node")
+		       + sizeof (bus_id));
+  char filename[len];
+  if (len < snprintf (filename, sizeof (filename),
+		     "/sys/bus/pci/devices/%s/numa_node", bus_id))
+    return -1;
+
+  FILE *in = fopen (filename, "r");
+  if (!in)
+    return -1;
+  int numa_node = -1;
+  fscanf (in, "%d", &numa_node);
+  fclose (in);
+
+  dev->numa_node = numa_node >= 0 ? numa_node + 1 : numa_node;
+  return numa_node;
+}
+
 unsigned int
 GOMP_OFFLOAD_get_caps (void)
 {
diff --git a/libgomp/target.c b/libgomp/target.c
index fdc488e94f7a..c9d4ef162d98 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -6085,8 +6085,60 @@ omp_get_device_from_uid (const char *uid)
   return omp_invalid_device;
 }
 
+/* Return the numa distance between the numa node of the calling host
+   thread and each of the NDEV devices in DEVICES.
+   Special values:
+   - Invalid device number: undefined, GCC uses -1.
+   - Host device: OpenMP defines this to be 0
+   - Nonhost device:  >= 0. If not available use 10 (= lowest ACPI
+     distance).
+   Note: Not available can mean either not a supported system
+   (e.g non Linux) or the value is not known (virtualized system). */
+
+void
+omp_get_device_distances (int ndevs, const int *devs, int *distances)
+{
+  if (ndevs < 1)
+    return;
+  int num_devices = gomp_get_num_devices ();
+  int node = gomp_get_current_numa_node ();
+  if (node < 0) /* Not supported. */
+    {
+      for (int i = 0; i < ndevs; i++)
+	if (devs[i] < omp_initial_device || devs[i] > num_devices)
+	  distances[i] = -1;  /* invalid */
+	else if (devs[i] == omp_initial_device || devs[i] == num_devices)
+	  distances[i] = 0;
+	else
+	  distances[i] = 10;
+      return;
+    }
+  for (int i = 0; i < ndevs; i++)
+    {
+      int device_num = (devs[i] == omp_default_device
+			? gomp_get_default_device () : devs[i]);
+      if (device_num < omp_initial_device || device_num > num_devices)
+	distances[i] = -1;  /* invalid */
+      else if (device_num == omp_initial_device || device_num == num_devices)
+	distances[i] = 0;
+      else
+	{
+	  int dist = 10;
+	  struct gomp_device_descr *devicep = resolve_device (device_num,
+							      false);
+	  if (devicep && devicep->get_numa_node_func)
+	    {
+	      int node2 = devicep->get_numa_node_func (devicep->target_id);
+	      dist = gomp_get_numa_distance (node, node2);
+	    }
+	  distances[i] = dist >= 0 ? dist : 10;
+	}
+    }
+}
+
 ialias (omp_get_uid_from_device)
 ialias (omp_get_device_from_uid)
+ialias (omp_get_device_distances)
 
 #ifdef PLUGIN_SUPPORT
 
@@ -6131,6 +6183,7 @@ gomp_load_plugin_for_device (struct gomp_device_descr *device,
 
   DLSYM (get_name);
   DLSYM_OPT (get_uid, get_uid);
+  DLSYM_OPT (get_numa_node, get_numa_node);
   DLSYM (get_caps);
   DLSYM (get_type);
   DLSYM (get_num_devices);
diff --git a/libgomp/testsuite/libgomp.c/omp-get-device-distances.c b/libgomp/testsuite/libgomp.c/omp-get-device-distances.c
new file mode 100644
index 000000000000..f1e5b468214f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/omp-get-device-distances.c
@@ -0,0 +1,101 @@
+#include <omp.h>
+
+int
+main ()
+{
+  int dev, dist;
+  int dev2, dist2;
+
+  // GCC specific: invalid numbers return -1
+  dev = omp_invalid_device; dist = -99;
+  omp_get_device_distances (1, &dev, &dist);
+  if (dev != omp_invalid_device || dist != -1)
+    __builtin_abort ();
+
+  dev = -99; dist = -99;
+  omp_get_device_distances (1, &dev, &dist);
+  if (dev != -99 || dist != -1)
+    __builtin_abort ();
+
+  dev = omp_get_num_devices () + 1; dist = -99;
+  omp_get_device_distances (1, &dev, &dist);
+  if (dev != omp_get_num_devices () + 1 || dist != -1)
+    __builtin_abort ();
+
+  // The following two need to yield idential results
+  dev = omp_default_device; dist = -99;
+  omp_get_device_distances (1, &dev, &dist);
+  if (dev != omp_default_device || dist < 0)
+    __builtin_abort ();
+
+  dev2 = omp_get_default_device (); dist2 = -99;
+  omp_get_device_distances (1, &dev2, &dist2);
+  if (dev2 != omp_get_default_device () || dist2 != dist)
+    __builtin_abort ();
+
+  // The following two need to yield idential results
+  dev = omp_initial_device; dist = -99;
+  omp_get_device_distances (1, &dev, &dist);
+  if (dev != omp_initial_device || dist != 0)
+    __builtin_abort ();
+
+  dev2 = omp_get_num_devices (); dist2 = -99;
+  omp_get_device_distances (1, &dev2, &dist2);
+  if (dev2 != omp_get_num_devices () || dist2 != 0)
+    __builtin_abort ();
+
+  for (int i = omp_initial_device; i <= omp_get_num_devices (); i++)
+    {
+      dev = i; dist = -99;
+      omp_get_device_distances (1, &dev, &dist);
+      if (dev != i)
+	__builtin_abort ();
+      // Host == 0 per definition
+      // GCC specific: All others (unknown or not) should have > 0
+      // the spec only requires >= 0
+      if (i == omp_initial_device || i == omp_get_num_devices ())
+	{
+	  if (dist != 0)
+	    __builtin_abort ();
+	}
+      else
+	{
+	  if (dist <= 0)
+	    __builtin_abort ();
+	}
+     }
+
+  int *devs, *dists;
+  int size = 1 + 1 + omp_get_num_devices ();
+  devs = (int *) __builtin_malloc (sizeof (int) * size * size);
+  dists = (int *) __builtin_malloc (sizeof (int) * size * size);
+  for (int i = 0; i < size; i++)
+    {
+      int dev = i - 1; // -1 (initial), 0, ... num_dev
+      devs[i] = dev;
+      dists[i] = -99;
+    }
+
+  omp_get_device_distances (size, devs, dists);
+
+  for (int i = 0; i < size; i++)
+    {
+      int dev = i - 1; // -1 (initial), 0, ... num_dev
+      if (devs[i] != dev)
+	__builtin_abort ();
+      // Host == 0 per definition
+      // GCC specific: All others (unknown or not) should have > 0
+      // the spec only requires >= 0
+      if (devs[i] == omp_initial_device || devs[i] == omp_get_num_devices ())
+	{
+	  if (dists[i] != 0)
+	    __builtin_abort ();
+	}
+      else
+	{
+	  if (dists[i] <= 0)
+	    __builtin_abort ();
+	}
+      __builtin_printf ("Device%3d: distance %d\n", dev, dists[i]);
+    }
+}
diff --git a/libgomp/testsuite/libgomp.fortran/omp-get-device-distances.f90 b/libgomp/testsuite/libgomp.fortran/omp-get-device-distances.f90
new file mode 100644
index 000000000000..8921cfd5a767
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/omp-get-device-distances.f90
@@ -0,0 +1,53 @@
+use omp_lib
+implicit none (type, external)
+integer :: devs(1), dists(1)
+integer, allocatable :: a_devs(:), a_dists(:)
+integer :: i
+
+! GCC specific: invalid numbers return -1
+devs(1) = omp_invalid_device; dists(1) = -99
+call omp_get_device_distances (size(devs), devs, dists)
+if (devs(1) /= omp_invalid_device .or. dists(1) /= -1) stop 1
+
+devs(1) = -123; dists(1) = -99
+call omp_get_device_distances (size(devs), devs, dists)
+if (devs(1) /= -123 .or. dists(1) /= -1) stop 2
+
+devs(1) = omp_get_num_devices () + 1; dists(1) = -99
+call omp_get_device_distances (size(devs), devs, dists)
+if (devs(1) /= omp_get_num_devices () + 1 .or. dists(1) /= -1) stop 3
+
+do i = omp_initial_device, omp_get_num_devices ()
+  devs(1) = i; dists(1) = -99
+  call omp_get_device_distances (size(devs), devs, dists)
+  if (devs(1) /= i) stop 4
+  ! Host == 0 per definition
+  ! GCC specific: All others (unknown or not) should have > 0
+  ! the spec only requires >= 0
+  if (i == omp_initial_device .or. i == omp_get_num_devices ()) then
+    if (dists(1) /= 0) stop 5
+  else
+    if (dists(1) <= 0) stop 6
+  end if
+end do
+
+allocate(a_devs(omp_initial_device:omp_get_num_devices ()))
+a_devs = [(i, i = omp_initial_device, omp_get_num_devices ())]
+allocate(a_dists, mold=a_devs)
+a_dists = -99
+
+call omp_get_device_distances (size(a_devs), a_devs, a_dists)
+
+do i = lbound (a_devs, 1), ubound (a_devs, 1)
+  if (a_devs(i) /= i) stop 7
+  ! Host == 0 per definition
+  ! GCC specific: All others (unknown or not) should have > 0
+  if (i == omp_initial_device .or. i == omp_get_num_devices ()) then
+    if (a_dists(i) /= 0) stop 8
+  else
+    if (a_dists(i) <= 0) stop 9
+  end if
+  print '(a,i3,a,i0)', 'device', i, ': distance ', a_dists(i)
+end do
+
+end
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.