From 6f1855281c38993a088f9b4af020a786f8e05fe9 Mon Sep 17 00:00:00 2001
From: Frederik Harwath <frederik@codesourcery.com>
Date: Tue, 28 Jan 2020 08:01:00 +0100
Subject: [PATCH] Add OpenACC acc_get_property support for AMD GCN

Add full support for the OpenACC 2.6 acc_get_property and
acc_get_property_string functions to the libgomp GCN plugin.

libgomp/
	* plugin-gcn.c (struct agent_info): Add fields "name" and
	"vendor_name" ...
	(GOMP_OFFLOAD_init_device): ... and init from here.
	(struct hsa_context_info): Add field "driver_version_s" ...
	(init_hsa_contest): ... and init from here.
	(GOMP_OFFLOAD_openacc_get_property): Replace stub with a proper
	implementation.
	* testsuite/libgomp.oacc-c-c++-common/acc_get_property.c:
	Enable test execution for amdgcn and host offloading targets.
	* testsuite/libgomp.oacc-fortran/acc_get_property.f90: Likewise.
	* testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c
	(expect_device_properties): Split function into ...
	(expect_device_string_properties): ... this new function ...
	(expect_device_memory): ... and this new function.
	* testsuite/libgomp.oacc-c-c++-common/acc_get_property-gcn.c:
	Add test.
---
 libgomp/plugin/plugin-gcn.c                   |  63 +++++++--
 .../acc_get_property-aux.c                    |  60 +++++---
 .../acc_get_property-gcn.c                    | 132 ++++++++++++++++++
 .../acc_get_property.c                        |   5 +-
 .../libgomp.oacc-fortran/acc_get_property.f90 |   2 -
 5 files changed, 224 insertions(+), 38 deletions(-)
 create mode 100644 libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-gcn.c

diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
index 7854c142f05..0a09daaa0a4 100644
--- a/libgomp/plugin/plugin-gcn.c
+++ b/libgomp/plugin/plugin-gcn.c
@@ -425,7 +425,10 @@ struct agent_info
 
   /* The instruction set architecture of the device. */
   gcn_isa device_isa;
-
+  /* Name of the agent. */
+  char name[64];
+  /* Name of the vendor of the agent. */
+  char vendor_name[64];
   /* Command queues of the agent.  */
   hsa_queue_t *sync_queue;
   struct goacc_asyncqueue *async_queues, *omp_async_queue;
@@ -544,6 +547,8 @@ struct hsa_context_info
   int agent_count;
   /* Array of agent_info structures describing the individual HSA agents.  */
   struct agent_info *agents;
+  /* Driver version string. */
+  char driver_version_s[30];
 };
 
 /* Format of the on-device heap.
@@ -1513,6 +1518,15 @@ init_hsa_context (void)
 	GOMP_PLUGIN_error ("Failed to list all HSA runtime agents");
     }
 
+  uint16_t minor, major;
+  status = hsa_fns.hsa_system_get_info_fn (HSA_SYSTEM_INFO_VERSION_MINOR, &minor);
+  if (status != HSA_STATUS_SUCCESS)
+    GOMP_PLUGIN_error ("Failed to obtain HSA runtime minor version");
+  status = hsa_fns.hsa_system_get_info_fn (HSA_SYSTEM_INFO_VERSION_MAJOR, &major);
+  if (status != HSA_STATUS_SUCCESS)
+    GOMP_PLUGIN_error ("Failed to obtain HSA runtime major version");
+  sprintf (hsa_context.driver_version_s, "HSA Runtime %d.%d", major, minor);
+
   hsa_context.initialized = true;
   return true;
 }
@@ -3410,15 +3424,19 @@ GOMP_OFFLOAD_init_device (int n)
     return hsa_error ("Error requesting maximum queue size of the GCN agent",
 		      status);
 
-  char buf[64];
   status = hsa_fns.hsa_agent_get_info_fn (agent->id, HSA_AGENT_INFO_NAME,
-					  &buf);
+					  &agent->name);
   if (status != HSA_STATUS_SUCCESS)
     return hsa_error ("Error querying the name of the agent", status);
 
-  agent->device_isa = isa_code (buf);
+  agent->device_isa = isa_code (agent->name);
   if (agent->device_isa < 0)
-    return hsa_error ("Unknown GCN agent architecture.", HSA_STATUS_ERROR);
+    return hsa_error ("Unknown GCN agent architecture", HSA_STATUS_ERROR);
+
+  status = hsa_fns.hsa_agent_get_info_fn (agent->id, HSA_AGENT_INFO_VENDOR_NAME,
+					  &agent->vendor_name);
+  if (status != HSA_STATUS_SUCCESS)
+    return hsa_error ("Error querying the vendor name of the agent", status);
 
   status = hsa_fns.hsa_queue_create_fn (agent->id, queue_size,
 					HSA_QUEUE_TYPE_MULTI,
@@ -4115,12 +4133,37 @@ GOMP_OFFLOAD_openacc_async_dev2host (int device, void *dst, const void *src,
 union goacc_property_value
 GOMP_OFFLOAD_openacc_get_property (int device, enum goacc_property prop)
 {
-  /* Stub. Check device and return default value for unsupported properties. */
-  /* TODO: Implement this function. */
-  get_agent_info (device);
+  struct agent_info *agent = get_agent_info (device);
+
+  union goacc_property_value propval = { .val = 0 };
+
+  switch (prop)
+    {
+    case GOACC_PROPERTY_FREE_MEMORY:
+      /* Not supported. */
+      break;
+    case GOACC_PROPERTY_MEMORY:
+      {
+	size_t size;
+	hsa_region_t region = agent->data_region;
+	hsa_status_t status =
+	  hsa_fns.hsa_region_get_info_fn (region, HSA_REGION_INFO_SIZE, &size);
+	if (status == HSA_STATUS_SUCCESS)
+	  propval.val = size;
+	break;
+      }
+    case GOACC_PROPERTY_NAME:
+      propval.ptr = agent->name;
+      break;
+    case GOACC_PROPERTY_VENDOR:
+      propval.ptr = agent->vendor_name;
+      break;
+    case GOACC_PROPERTY_DRIVER:
+      propval.ptr = hsa_context.driver_version_s;
+      break;
+    }
 
-  union goacc_property_value nullval = { .val = 0 };
-  return nullval;
+  return propval;
 }
 
 /* Set up plugin-specific thread-local-data (host-side).  */
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c
index 6bb01250148..6a4a499eb21 100644
--- a/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-aux.c
@@ -6,10 +6,11 @@
 #include <stdio.h>
 #include <string.h>
 
-void expect_device_properties
+
+void expect_device_string_properties
 (acc_device_t dev_type, int dev_num,
- size_t expected_memory, const char* expected_vendor,
- const char* expected_name, const char* expected_driver)
+ const char* expected_vendor, const char* expected_name,
+ const char* expected_driver)
 {
   const char *vendor = acc_get_property_string (dev_num, dev_type,
 						acc_property_vendor);
@@ -20,26 +21,6 @@ void expect_device_properties
       abort ();
     }
 
-  size_t total_mem = acc_get_property (dev_num, dev_type,
-				       acc_property_memory);
-  if (total_mem != expected_memory)
-    {
-      fprintf (stderr, "Expected acc_property_memory to equal %zu, "
-	       "but was %zu.\n", expected_memory, total_mem);
-      abort ();
-
-    }
-
-  size_t free_mem = acc_get_property (dev_num, dev_type,
-				   acc_property_free_memory);
-  if (free_mem > total_mem)
-    {
-      fprintf (stderr, "Expected acc_property_free_memory <= acc_property_memory"
-	       ", but free memory was %zu and total memory was %zu.\n",
-	       free_mem, total_mem);
-      abort ();
-    }
-
   const char *name = acc_get_property_string (dev_num, dev_type,
 					      acc_property_name);
   if (strcmp (name, expected_name))
@@ -75,6 +56,39 @@ void expect_device_properties
 	       "but was %s.\n", s);
       abort ();
     }
+}
+
+void expect_device_memory
+(acc_device_t dev_type, int dev_num, size_t expected_total_memory)
+{
+
+  size_t total_mem = acc_get_property (dev_num, dev_type,
+				       acc_property_memory);
+
+  if (total_mem != expected_total_memory)
+    {
+      fprintf (stderr, "Expected acc_property_memory to equal %zu, "
+	       "but was %zu.\n", expected_total_memory, total_mem);
+      abort ();
+    }
 
+  size_t free_mem = acc_get_property (dev_num, dev_type,
+				      acc_property_free_memory);
+  if (free_mem > total_mem)
+    {
+      fprintf (stderr, "Expected acc_property_free_memory <= acc_property_memory"
+	       ", but free memory was %zu and total memory was %zu.\n",
+	       free_mem, total_mem);
+      abort ();
+    }
+}
 
+void expect_device_properties
+(acc_device_t dev_type, int dev_num,
+ size_t expected_total_memory, const char* expected_vendor,
+ const char* expected_name, const char* expected_driver)
+{
+  expect_device_string_properties (dev_type, dev_num, expected_vendor,
+				   expected_name, expected_driver);
+  expect_device_memory (dev_type, dev_num, expected_total_memory);
 }
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-gcn.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-gcn.c
new file mode 100644
index 00000000000..305bb1af365
--- /dev/null
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property-gcn.c
@@ -0,0 +1,132 @@
+/* Test the `acc_get_property' and `acc_get_property_string' library
+   functions on amdgcn devices by comparing property values with
+   those obtained through the HSA API. */
+/* { dg-additional-sources acc_get_property-aux.c } */
+/* { dg-additional-options "-ldl" } */
+/* { dg-do run { target openacc_amdgcn_accel_selected } } */
+
+#include <dlfcn.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <openacc.h>
+
+#ifndef __cplusplus
+typedef int bool;
+#endif
+#include <hsa.h>
+
+
+void expect_device_string_properties
+(acc_device_t dev_type, int dev_num,
+ const char* expected_vendor, const char* expected_name,
+ const char* expected_driver);
+
+hsa_status_t (*hsa_agent_get_info_fn) (hsa_agent_t agent,
+				       hsa_agent_info_t attribute,
+				       void *value);
+hsa_status_t (*hsa_system_get_info_fn) (hsa_system_info_t attribute,
+					void *value);
+hsa_status_t (*hsa_iterate_agents_fn)
+(hsa_status_t (*callback)(hsa_agent_t agent, void *data), void *data);
+hsa_status_t (*hsa_init_fn) (void);
+
+char* support_cpu_devices;
+
+void test_setup ()
+{
+  char* env_runtime;
+  char* hsa_runtime_lib;
+  void *handle;
+
+#define DLSYM_FN(function)						\
+  function##_fn = (typeof(function##_fn))dlsym (handle, #function);	\
+  if (function##_fn == NULL)						\
+    {									\
+      fprintf (stderr, "Could not get symbol " #function ".\n");	\
+      abort (); 							\
+    }
+
+  env_runtime = getenv ("HSA_RUNTIME_LIB");
+  hsa_runtime_lib = env_runtime ? env_runtime : (char*)"libhsa-runtime64.so";
+
+  handle = dlopen (hsa_runtime_lib, RTLD_LAZY);
+  if (!handle)
+    {
+      fprintf (stderr, "Could not load %s.\n", hsa_runtime_lib);
+      abort ();
+    }
+
+  DLSYM_FN (hsa_agent_get_info)
+  DLSYM_FN (hsa_system_get_info)
+  DLSYM_FN (hsa_iterate_agents)
+  DLSYM_FN (hsa_init)
+
+  hsa_init_fn ();
+
+  support_cpu_devices = getenv ("GCN_SUPPORT_CPU_DEVICES");
+}
+
+static hsa_status_t check_agent_properties
+(hsa_agent_t agent, void *dev_num_arg)
+{
+
+  char name[64];
+  char vendor_name[64];
+  uint16_t minor;
+  uint16_t major;
+  char driver[30];
+
+  hsa_status_t status;
+  hsa_device_type_t device_type;
+  int* dev_num = (int*)dev_num_arg;
+
+#define AGENT_GET_INFO(info_type, val)				\
+  status = hsa_agent_get_info_fn (agent, info_type, &val);	\
+  if (status != HSA_STATUS_SUCCESS)				\
+    {								\
+      fprintf (stderr, "Failed to obtain " #info_type ".\n");	\
+      abort ();							\
+    }
+#define SYSTEM_GET_INFO(info_type, val)				\
+  status = hsa_system_get_info_fn (info_type, &val);		\
+  if (status != HSA_STATUS_SUCCESS)				\
+    {								\
+      fprintf (stderr, "Failed to obtain " #info_type ".\n");	\
+      abort ();							\
+    }
+
+  AGENT_GET_INFO (HSA_AGENT_INFO_DEVICE, device_type)
+
+    /* Skip unsupported device types.  Mimic the GCN plugin's behavior. */
+    if (!(device_type == HSA_DEVICE_TYPE_GPU
+	  || (support_cpu_devices && device_type == HSA_DEVICE_TYPE_CPU)))
+      return HSA_STATUS_SUCCESS;
+
+  AGENT_GET_INFO (HSA_AGENT_INFO_NAME, name)
+  AGENT_GET_INFO (HSA_AGENT_INFO_VENDOR_NAME, vendor_name)
+
+  SYSTEM_GET_INFO (HSA_SYSTEM_INFO_VERSION_MINOR, minor)
+  SYSTEM_GET_INFO (HSA_SYSTEM_INFO_VERSION_MAJOR, major)
+
+  sprintf (driver, "HSA Runtime %d.%d", major, minor);
+
+  expect_device_string_properties(acc_device_radeon, *dev_num,
+				  vendor_name, name, driver);
+
+  (*dev_num)++;
+
+  return status;
+}
+
+int main()
+{
+  int dev_num = 0;
+  test_setup ();
+
+  hsa_status_t status =
+    hsa_iterate_agents_fn (&check_agent_properties, &dev_num);
+
+  return status;
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property.c
index 388c66c1319..1984ad3a28d 100644
--- a/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property.c
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/acc_get_property.c
@@ -3,8 +3,7 @@
    of all device types mentioned in the OpenACC standard.
 
    See also acc_get_property.f90. */
-/* { dg-do run { target { { ! { openacc_host_selected } } && { ! { openacc_amdgcn_accel_selected } } } } } */
-/* FIXME: This test does not work with the GCN implementation stub yet.  */
+/* { dg-do run } */
 
 #include <openacc.h>
 #include <stdlib.h>
@@ -20,7 +19,7 @@ print_device_properties(acc_device_t type)
   const char *s;
   size_t v;
 
-  int dev_count = acc_get_num_devices(type);
+  int dev_count = acc_get_num_devices (type);
 
   for (int i = 0; i < dev_count; ++i)
     {
diff --git a/libgomp/testsuite/libgomp.oacc-fortran/acc_get_property.f90 b/libgomp/testsuite/libgomp.oacc-fortran/acc_get_property.f90
index ce695475ae4..80ae292f41f 100644
--- a/libgomp/testsuite/libgomp.oacc-fortran/acc_get_property.f90
+++ b/libgomp/testsuite/libgomp.oacc-fortran/acc_get_property.f90
@@ -3,8 +3,6 @@
 ! of all device types mentioned in the OpenACC standard.
 !
 ! See also acc_get_property.c
-! { dg-do run { target { { ! { openacc_host_selected } } && { ! { openacc_amdgcn_accel_selected } } } } }
-! FIXME: This test does not work with the GCN implementation stub yet.
 
 program test
   use openacc
-- 
2.17.1


