Add runtime ISA check for amdgcn offloading
authorFrederik Harwath <frederik@codesourcery.com>
Mon, 20 Jan 2020 06:45:43 +0000 (07:45 +0100)
committerFrederik Harwath <frederik@codesourcery.com>
Tue, 21 Jan 2020 06:41:45 +0000 (07:41 +0100)
The HSA/ROCm runtime rejects binaries not built for the exact GPU device
present. So far, the libgomp amdgcn plugin does not verify that the GPU ISA
and the ISA specified at compile time match before handing over the binary to
the runtime.  In case of a mismatch, the user is confronted with an unhelpful
runtime error.

This commit implements a runtime ISA check. In case of an 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" and remove field
"gfx900_p".
* (GOMP_OFFLOAD_init_device): Adapt agent init to "agent_info"
field changes, fail if device has unknown ISA.
* (parse_target_attributes): Replace "gfx900_p" by "device_isa".
* (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

index 16ce251f3a5600c6e067eb64eb122d4b5a98647d..de470a3dd33c554117529314263a3e05529292e9 100644 (file)
@@ -396,6 +396,20 @@ 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;
+
 /* Description of an HSA GPU agent (device) and the program associated with
    it.  */
 
@@ -408,8 +422,9 @@ struct agent_info
   /* Whether the agent has been initialized.  The fields below are usable only
      if it has been.  */
   bool initialized;
-  /* 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;
@@ -1232,7 +1247,8 @@ parse_target_attributes (void **input,
 
   if (gcn_dims_found)
     {
-      if (agent->gfx900_p && gcn_threads == 0 && override_z_dim == 0)
+      if (agent->device_isa == EF_AMDGPU_MACH_AMDGCN_GFX900
+         && gcn_threads == 0 && override_z_dim == 0)
        {
          gcn_threads = 4;
          GCN_WARNING ("VEGA BUG WORKAROUND: reducing default number of "
@@ -1578,6 +1594,74 @@ get_data_memory_region (hsa_region_t region, void *data)
                            HSA_REGION_GLOBAL_FLAG_COARSE_GRAINED);
 }
 
+static int
+elf_gcn_isa_field (Elf64_Ehdr *image)
+{
+  return image->e_flags & EF_AMDGPU_MACH_MASK;
+}
+
+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;
+
+/* 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;
+}
+
 /* }}}  */
 /* {{{ Run  */
 
@@ -2257,6 +2341,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 +2406,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 +3414,10 @@ 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->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,