From 27981f9c93d1efed6d943dae4ea0c52147c02d5b Mon Sep 17 00:00:00 2001
From: Frederik Harwath <frederik@codesourcery.com>
Date: Mon, 20 Jan 2020 07:45:43 +0100
Subject: [PATCH] Add runtime ISA check for amdgcn offloading

When executing code that uses amdgcn GPU offloading, the ISA of the GPU must
match the ISA for which the code has been compiled.  So far, the libgomp amdgcn
plugin did not attempt to verify this.  In case of a mismatch, the user is
confronted with an unhelpful error message produced by the HSA runtime.

This commit implements a runtime ISA check. In the case of a ISA mismatch, the
execution is aborted with a clear error message and a hint at the correct
compilation parameters for the GPU on which the execution has been attempted.

libgomp/
	* plugin/plugin-gcn.c (EF_AMDGPU_MACH): New enum.
	(EF_AMDGPU_MACH_MASK): New constant.
	(gcn_isa): New typedef.
	(gcn_gfx801_s): New constant.
	(gcn_gfx803_s): New constant.
	(gcn_gfx900_s): New constant.
	(gcn_gfx906_s): New constant.
	(gcn_isa_name_len): New constant.
	(elf_gcn_isa_field): New function.
	(isa_hsa_name): New function.
	(isa_gcc_name): New function.
	(isa_code): New function.
	(struct agent_info): Add field "device_isa" ...
	(GOMP_OFFLOAD_init_device): ... and init from here,
	failing if device has unknown ISA; adapt init of "gfx900_p"
	to use new constants.
	(isa_matches_agent): New function ...
	(create_and_finalize_hsa_program): ... used from here to check
	that the GPU ISA and the code-object ISA match.
---
 libgomp/plugin/plugin-gcn.c | 127 +++++++++++++++++++++++++++++++++++-
 1 file changed, 126 insertions(+), 1 deletion(-)

diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
index 16ce251f3a5..14f4a707a7c 100644
--- a/libgomp/plugin/plugin-gcn.c
+++ b/libgomp/plugin/plugin-gcn.c
@@ -396,6 +396,88 @@ struct gcn_image_desc
   struct global_var_info *global_variables;
 };
 
+/* This enum mirrors the corresponding LLVM enum's values for all ISAs that we
+   support.
+   See https://llvm.org/docs/AMDGPUUsage.html#amdgpu-ef-amdgpu-mach-table */
+
+typedef enum {
+  EF_AMDGPU_MACH_AMDGCN_GFX801 = 0x028,
+  EF_AMDGPU_MACH_AMDGCN_GFX803 = 0x02a,
+  EF_AMDGPU_MACH_AMDGCN_GFX900 = 0x02c,
+  EF_AMDGPU_MACH_AMDGCN_GFX906 = 0x02f,
+} EF_AMDGPU_MACH;
+
+const static int EF_AMDGPU_MACH_MASK = 0x000000ff;
+typedef EF_AMDGPU_MACH gcn_isa;
+
+const static char* gcn_gfx801_s = "gfx801";
+const static char* gcn_gfx803_s = "gfx803";
+const static char* gcn_gfx900_s = "gfx900";
+const static char* gcn_gfx906_s = "gfx906";
+const static int gcn_isa_name_len = 6;
+
+static int
+elf_gcn_isa_field (Elf64_Ehdr *image)
+{
+  return image->e_flags & EF_AMDGPU_MACH_MASK;
+}
+
+/* Returns the name that the HSA runtime uses for the ISA or NULL if we do not
+   support the ISA. */
+
+static const char*
+isa_hsa_name (int isa) {
+  switch(isa)
+    {
+    case EF_AMDGPU_MACH_AMDGCN_GFX801:
+      return gcn_gfx801_s;
+    case EF_AMDGPU_MACH_AMDGCN_GFX803:
+      return gcn_gfx803_s;
+    case EF_AMDGPU_MACH_AMDGCN_GFX900:
+      return gcn_gfx900_s;
+    case EF_AMDGPU_MACH_AMDGCN_GFX906:
+      return gcn_gfx906_s;
+    }
+  return NULL;
+}
+
+/* Returns the user-facing name that GCC uses to identify the architecture (e.g.
+   with -march) or NULL if we do not support the ISA.
+   Keep in sync with /gcc/config/gcn/gcn.{c,opt}.  */
+
+static const char*
+isa_gcc_name (int isa) {
+  switch(isa)
+    {
+    case EF_AMDGPU_MACH_AMDGCN_GFX801:
+      return "carrizo";
+    case EF_AMDGPU_MACH_AMDGCN_GFX803:
+      return "fiji";
+    default:
+      return isa_hsa_name (isa);
+    }
+}
+
+/* Returns the code which is used in the GCN object code to identify the ISA with
+   the given name (as used by the HSA runtime).  */
+
+static gcn_isa
+isa_code(const char *isa) {
+  if (!strncmp (isa, gcn_gfx801_s, gcn_isa_name_len))
+    return EF_AMDGPU_MACH_AMDGCN_GFX801;
+
+  if (!strncmp (isa, gcn_gfx803_s, gcn_isa_name_len))
+    return EF_AMDGPU_MACH_AMDGCN_GFX803;
+
+  if (!strncmp (isa, gcn_gfx900_s, gcn_isa_name_len))
+    return EF_AMDGPU_MACH_AMDGCN_GFX900;
+
+  if (!strncmp (isa, gcn_gfx906_s, gcn_isa_name_len))
+    return EF_AMDGPU_MACH_AMDGCN_GFX906;
+
+  return -1;
+}
+
 /* Description of an HSA GPU agent (device) and the program associated with
    it.  */
 
@@ -411,6 +493,9 @@ struct agent_info
   /* Precomputed check for problem architectures.  */
   bool gfx900_p;
 
+  /* The instruction set architecture of the device. */
+  gcn_isa device_isa;
+
   /* Command queues of the agent.  */
   hsa_queue_t *sync_queue;
   struct goacc_asyncqueue *async_queues, *omp_async_queue;
@@ -2257,6 +2342,39 @@ find_load_offset (Elf64_Addr *load_offset, struct agent_info *agent,
   return res;
 }
 
+/* Check that the GCN ISA of the given image matches the ISA of the agent. */
+
+static bool
+isa_matches_agent (struct agent_info *agent, Elf64_Ehdr *image)
+{
+      int isa_field = elf_gcn_isa_field (image);
+      const char* isa_s = isa_hsa_name (isa_field);
+      if (!isa_s)
+	{
+	  hsa_error ("Unsupported ISA in GCN code object.", HSA_STATUS_ERROR);
+	  return false;
+	}
+
+      if (isa_field != agent->device_isa)
+	{
+	  char msg[120];
+	  const char *agent_isa_s = isa_hsa_name (agent->device_isa);
+	  const char *agent_isa_gcc_s = isa_gcc_name (agent->device_isa);
+	  assert (agent_isa_s);
+	  assert (agent_isa_gcc_s);
+
+	  snprintf (msg, sizeof msg,
+		    "GCN code object ISA '%s' does not match GPU ISA '%s'.\n"
+		    "Try to recompile with '-foffload=-march=%s'.\n",
+		    isa_s, agent_isa_s, agent_isa_gcc_s);
+
+	  hsa_error (msg, HSA_STATUS_ERROR);
+	  return false;
+	}
+
+      return true;
+}
+
 /* Create and finalize the program consisting of all loaded modules.  */
 
 static bool
@@ -2289,6 +2407,9 @@ create_and_finalize_hsa_program (struct agent_info *agent)
     {
       Elf64_Ehdr *image = (Elf64_Ehdr *)module->image_desc->gcn_image->image;
 
+      if (!isa_matches_agent (agent, image))
+	goto fail;
+
       /* Hide relocations from the HSA runtime loader.
 	 Keep a copy of the unmodified section headers to use later.  */
       Elf64_Shdr *image_sections = (Elf64_Shdr *)((char *)image
@@ -3294,7 +3415,11 @@ GOMP_OFFLOAD_init_device (int n)
 					  &buf);
   if (status != HSA_STATUS_SUCCESS)
     return hsa_error ("Error querying the name of the agent", status);
-  agent->gfx900_p = (strncmp (buf, "gfx900", 6) == 0);
+  agent->gfx900_p = (strncmp (buf, gcn_gfx900_s, gcn_isa_name_len) == 0);
+
+  agent->device_isa = isa_code (buf);
+  if (agent->device_isa < 0)
+    return hsa_error ("Unknown GCN agent architecture.", HSA_STATUS_ERROR);
 
   status = hsa_fns.hsa_queue_create_fn (agent->id, queue_size,
 					HSA_QUEUE_TYPE_MULTI,
-- 
2.17.1

