[Bug middle-end/88703] New: oacc_validate_dims allows invalid dimensions

vries at gcc dot gnu.org gcc-bugzilla@gcc.gnu.org
Sat Jan 5 00:52:00 GMT 2019


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88703

            Bug ID: 88703
           Summary: oacc_validate_dims allows invalid dimensions
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: vries at gcc dot gnu.org
  Target Milestone: ---

Consider oacc_validate_dims on trunk:
...
oacc_validate_dims (tree fn, tree attrs, int *dims, int level, unsigned used)
{
  tree purpose[GOMP_DIM_MAX];
  unsigned ix;
  tree pos = TREE_VALUE (attrs);

  /* Make sure the attribute creator attached the dimension                     
     information.  */
  gcc_assert (pos);

  for (ix = 0; ix != GOMP_DIM_MAX; ix++)
    {
      purpose[ix] = TREE_PURPOSE (pos);
      tree val = TREE_VALUE (pos);
      dims[ix] = val ? TREE_INT_CST_LOW (val) : -1;
      pos = TREE_CHAIN (pos);
    }

  bool changed = targetm.goacc.validate_dims (fn, dims, level);

  /* Default anything left to 1 or a partitioned default.  */
  for (ix = 0; ix != GOMP_DIM_MAX; ix++)
    if (dims[ix] < 0)
      {
        /* The OpenACC spec says 'If the [num_gangs] clause is not              
           specified, an implementation-defined default will be used;           
           the default may depend on the code within the construct.'            
           (2.5.6).  Thus an implementation is free to choose                   
           non-unity default for a parallel region that doesn't have            
           any gang-partitioned loops.  However, it appears that there          
           is a sufficient body of user code that expects non-gang              
           partitioned regions to not execute in gang-redundant mode.           
           So we (a) don't warn about the non-portability and (b) pick          
           the minimum permissible dimension size when there is no              
           partitioned execution.  Otherwise we pick the global                 
           default for the dimension, which the user can control.  The          
           same wording and logic applies to num_workers and                    
           vector_length, however the worker- or vector- single                 
           execution doesn't have the same impact as gang-redundant             
           execution.  (If the minimum gang-level partioning is not 1,          
           the target is probably too confusing.)  */
        dims[ix] = (used & GOMP_DIM_MASK (ix)
                    ? oacc_default_dims[ix] : oacc_min_dims[ix]);
        changed = true;
      }

  if (changed)
    {
      /* Replace the attribute with new values.  */
      pos = NULL_TREE;
      for (ix = GOMP_DIM_MAX; ix--;)
        pos = tree_cons (purpose[ix],
                         build_int_cst (integer_type_node, dims[ix]), pos);
      oacc_replace_fn_attrib (fn, pos);
    }
}
...

It does the following:
- read the dimensions set in the attributes
- call targetm.goacc.validate_dims on those dimensions
- apply oacc_default_dims[ix] or oacc_min_dims[ix] to set remaining unset
  dimensions
- update the dimensions in the attributes

However, it's possible that the resulting dimensions are in fact invalid.

Consider this test-case on og8 branch:
...
$ cat libgomp/testsuite/libgomp.oacc-c-c++-common/test.c
/* { dg-do run { target openacc_nvidia_accel_selected } } */
/* { dg-additional-options "-fopenacc-dim=:32
-foffload=-mlong-vector-in-workers" } */

#include <stdlib.h>

#define N 2048

unsigned int a[N];
unsigned int b[N];
unsigned int c[N];
unsigned int n = N;

int
main (void)
{
#pragma acc parallel vector_length (128) copyin (a,b) copyout (c)
  {
    #pragma acc loop worker
    for (unsigned int i = 0; i < n; i++)
      #pragma acc loop vector
      for (unsigned int j = 0; j < n; j++)
        ;
  }

  return 0;
}
...

This generates these dimensions in the .s file:
...
//:FUNC_MAP "main$_omp_fn$0", 0x1, 0x20, 0x80
...
so, num_workers * vector_length == 0x20 * 0x80 == 32 * 128 == 4096 (while the
maximum allowed is 1024 == maximum CTA size).

This causes a runtime error:
...
libgomp: The Nvidia accelerator has insufficient resources to launch
'main$_omp_fn$0' with num_workers = 32 and vector_length = 128; recompile the
program with 'num_workers = x and vector_length = y' on that offloaded region
or '-fopenacc-dim=-:x:y' where x * y <= 1024.
...

An easy way to detect this problem at compile time is by adding an assert here:
...
diff --git a/gcc/omp-offload.c b/gcc/omp-offload.c
index aac0aa8b27a..7e3efa1032e 100644
--- a/gcc/omp-offload.c
+++ b/gcc/omp-offload.c
@@ -700,6 +700,7 @@ oacc_validate_dims
                    ? oacc_default_dims[ix] : oacc_min_dims[ix]);
        changed = true;
       }
+  gcc_assert (!targetm.goacc.validate_dims (fn, dims, level));

   if (changed)
     {
...

For the test-case, the compiler will enter the second call to
targetm.goacc.validate_dims with dims {1, 32, 128}, which will be updated to
{1, 32, 32}, which will cause targetm.goacc.validate_dims to return true, which
will trigger the assert.

AFAIU, this is a generic problem with the targetm.goacc.validate_dims hook on
both trunk and og8, and not specific to nvptx.


More information about the Gcc-bugs mailing list