On Tue, Aug 02, 2022 at 09:52:02AM +0200, Marcel Vollweiler wrote:
> libgomp/ChangeLog:
> 
>       * config/gcn/icv-device.c (omp_get_default_device): Return device-
>       specific ICV.
>       (omp_get_max_teams): Added for GCN devices.
>       (omp_set_num_teams): Likewise.
>       (ialias): Likewise.
>       * config/nvptx/icv-device.c (omp_get_default_device): Return device-
>       specific ICV.
>       (omp_get_max_teams): Added for NVPTX devices.
>       (omp_set_num_teams): Likewise.
>       (ialias): Likewise.
>       * env.c (struct gomp_icv_list): New struct to store entries of initial
>       ICV values.
>       (struct gomp_offload_icv_list): New struct to store entries of device-
>       specific ICV values that are copied to the device and back.
>       (struct gomp_default_icv_t): New struct to store default values of ICVs
>       according to the OpenMP standard.
>       (parse_schedule): Generalized for different variants of OMP_SCHEDULE.
>       (print_env_var_error): Function that prints an error for invalid values
>       for ICVs.
>       (parse_unsigned_long_1): Removed getenv. Generalized.
>       (parse_unsigned_long): Likewise.
>       (parse_int_1): Likewise.
>       (parse_int): Likewise.
>       (parse_int_secure): Likewise.
>       (parse_unsigned_long_list): Likewise.
>       (parse_target_offload): Likewise.
>       (parse_bind_var): Likewise.
>       (parse_stacksize): Likewise.
>       (parse_boolean): Likewise.
>       (parse_wait_policy): Likewise.
>       (parse_allocator): Likewise.
>       (omp_display_env): Extended to output different variants of environment
>       variables.
>       (print_schedule): New helper function for omp_display_env which prints
>       the values of run_sched_var.
>       (print_proc_bind): New helper function for omp_display_env which prints
>       the values of proc_bind_var.
>       (enum gomp_parse_type): Collection of types used for parsing environment
>       variables.
>       (ENTRY): Preprocess string lengths of environment variables.
>       (OMP_VAR_CNT): Preprocess table size.
>       (OMP_HOST_VAR_CNT): Likewise.
>       (INT_MAX_STR_LEN): Constant for the maximal number of digits of a device
>       number.
>       (gomp_get_icv_flag): Returns if a flag for a particular ICV is set.
>       (gomp_set_icv_flag): Sets a flag for a particular ICV.
>       (print_device_specific_icvs): New helper function for omp_display_env to
>       print device specific ICV values.
>       (get_device_num): New helper function for parse_device_specific.
>       Extracts the device number from an environment variable name.
>       (get_icv_member_addr): Gets the memory address for a particular member
>       of an ICV struct.
>       (gomp_get_initial_icv_item): Get a list item of gomp_initial_icv_list.
>       (gomp_get_offload_icv_item): Get a list item of gomp_offload_icv_list.
>       (add_initial_icv_to_list): Adds an ICV struct to gomp_initial_icv_list.
>       (startswith): Checks if a string starts with a given prefix.
>       (initialize_env): Extended to parse the new syntax of environment
>       variables.
>       * icv-device.c (omp_get_max_teams): Added.
>       (ialias): Likewise.
>       (omp_set_num_teams): Likewise.
>       * icv.c (omp_set_num_teams): Moved to icv-device.c.
>       (omp_get_max_teams): Likewise.
>       (ialias): Likewise.
>       * libgomp-plugin.h (GOMP_DEVICE_NUM_VAR): Removed.
>       (GOMP_ADDITIONAL_ICVS): New target-side struct that
>       holds the designated ICVs of the target device.
>       * libgomp.h (enum gomp_icvs): Collection of ICVs.
>       (enum gomp_env_suffix): Collection of possible suffixes of environment
>       variables.
>       (struct gomp_initial_icvs): Contains all ICVs for which we need to store
>       initial values.
>       (struct gomp_default_icv_t): New struct to hold ICVs for which we need
>       to store initial values.
>       (struct gomp_icv_list): Definition of a linked list that is used for
>       storing ICVs for the devices and also for _DEV, _ALL, and without
>       suffix.
>       (gomp_get_initial_icv_item): Get a list item of gomp_initial_icv_list.
>       (gomp_get_icv_flag): Returns if a flag for a particular ICV is set.
>       (struct gomp_offload_icvs): New struct to hold ICVs that are copied to
>       a device.
>       (struct gomp_offload_icv_list): Definition of a linked list that holds
>       device-specific ICVs that are copied to devices.
>       (gomp_get_offload_icv_item): Get a list item of gomp_offload_icv_list.
>       * plugin/plugin-gcn.c (GOMP_OFFLOAD_load_image): Extended to read
>       further ICVs from the offload image.
>       * plugin/plugin-nvptx.c (GOMP_OFFLOAD_load_image): Likewise.
>       * target.c (get_gomp_offload_icvs): New. Returns the ICV values
>       depending on the device num and the variable hierarchy.
>       (gomp_load_image_to_device): Extended to copy further ICVs to a device.
>       * testsuite/libgomp.c-c++-common/icv-5.c: New test.
>       * testsuite/libgomp.c-c++-common/icv-6.c: New test.
>       * testsuite/libgomp.c-c++-common/icv-7.c: New test.
>       * testsuite/libgomp.c-c++-common/icv-8.c: New test.
>       * testsuite/libgomp.c-c++-common/omp-display-env-1.c: New test.
>       * testsuite/libgomp.c-c++-common/omp-display-env-2.c: New test.
> 
> +/* Default values of ICVs according to the OpenMP standard.  */
> +struct gomp_default_icv_t gomp_default_icv_values = {
> +  .run_sched_var = GFS_DYNAMIC,
> +  .run_sched_chunk_size = 1,
> +  .max_active_levels_var = 1,
> +  .bind_var = omp_proc_bind_false,
> +  .nteams_var = 0,
> +  .teams_thread_limit_var = 0,
> +  .default_device_var = 0
> +};

Why this var (and if it is really needed, why it isn't const)?
You seem to be using only 2 fields from it:
libgomp/libgomp.h:extern struct gomp_default_icv_t gomp_default_icv_values;
libgomp/env.c:struct gomp_default_icv_t gomp_default_icv_values = {
libgomp/target.c:    new->icvs.nteams = gomp_default_icv_values.nteams_var;
libgomp/target.c:    new->icvs.default_device = 
gomp_default_icv_values.default_device_var;

> +
>  bool gomp_cancel_var = false;
>  enum gomp_target_offload_t gomp_target_offload_var
>    = GOMP_TARGET_OFFLOAD_DEFAULT;
> @@ -104,86 +123,94 @@ int goacc_default_dims[GOMP_DIM_MAX];
>  static int wait_policy;
>  static unsigned long stacksize = GOMP_DEFAULT_STACKSIZE;
>  
> -/* Parse the OMP_SCHEDULE environment variable.  */
> -
>  static void
> -parse_schedule (void)
> +print_env_var_error (const char *env, const char *val)
>  {
> -  char *env, *end;
> +  char name[val - env];
> +  memcpy (name, env, val - env - 1);
> +  name[val - env - 1] = '\0';
> +  gomp_error ("Invalid value for environment variable %s: %s", name, val);

Why the temporary buffer (especially VLA)?
Just
  gomp_error ("Invalid value for environment variable %.*s: %s",
              (int) (val - env - 1), env, val);
should do the job.

> +/* Parse the OMP_SCHEDULE environment variable.  */
> +static bool
> +parse_schedule (const char *env, const char *val, void * const params[])

No space after *

> +#define ENTRY(NAME) NAME, sizeof (NAME) - 1
> +static const struct envvar
> +{
> +  const char *name;
> +  int name_len;
> +  uint8_t flag_vars[3];
> +  uint8_t flag;
> +  bool (*parse_func) (const char *, const char *, void * const[]);
> +} envvars[] = {
> +  { ENTRY ("OMP_SCHEDULE"),
> +    { GOMP_ICV_SCHEDULE, GOMP_ICV_SCHEDULE_CHUNK_SIZE },
> +    GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X,
> +    &parse_schedule },
> +  { ENTRY ("OMP_NUM_TEAMS"),
> +    { GOMP_ICV_NTEAMS },
> +    GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X,
> +    &parse_int },
> +  { ENTRY ("OMP_DYNAMIC"),
> +    { GOMP_ICV_DYNAMIC },
> +    GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X,
> +    &parse_boolean },
> +  { ENTRY ("OMP_TEAMS_THREAD_LIMIT"),
> +    { GOMP_ICV_TEAMS_THREAD_LIMIT },
> +    GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X,
> +    &parse_int },
> +  { ENTRY ("OMP_THREAD_LIMIT"),
> +    { GOMP_ICV_THREAD_LIMIT },
> +    GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X,
> +    &parse_unsigned_long },
> +  { ENTRY ("OMP_NUM_THREADS"),
> +    { GOMP_ICV_NTHREADS, GOMP_ICV_NTHREADS_LIST, GOMP_ICV_NTHREADS_LIST_LEN 
> },
> +    GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X,
> +    &parse_unsigned_long_list },
> +  { ENTRY ("OMP_PROC_BIND"),
> +    { GOMP_ICV_BIND, GOMP_ICV_BIND_LIST, GOMP_ICV_BIND_LIST_LEN },
> +    GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X,
> +    &parse_bind_var },
> +  { ENTRY ("OMP_MAX_ACTIVE_LEVELS"),
> +    { GOMP_ICV_MAX_ACTIVE_LEVELS },
> +    GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X,
> +    &parse_unsigned_long },
> +  { ENTRY ("OMP_WAIT_POLICY"),
> +    { GOMP_ICV_WAIT_POLICY },
> +    GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X,
> +    &parse_wait_policy },
> +  { ENTRY ("OMP_STACKSIZE"),
> +    { GOMP_ICV_STACKSIZE },
> +    GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X,
> +    &parse_stacksize },
> +  { ENTRY ("OMP_CANCELLATION"), { GOMP_ICV_CANCELLATION }, 0, &parse_boolean 
> },
> +  { ENTRY ("OMP_DISPLAY_AFFINITY"), { GOMP_ICV_DISPLAY_AFFINITY }, 0,
> +    &parse_boolean },
> +  { ENTRY ("OMP_TARGET_OFFLOAD"), { GOMP_ICV_TARGET_OFFLOAD }, 0,
> +    &parse_target_offload },
> +  { ENTRY ("OMP_MAX_TASK_PRIORITY"), { GOMP_ICV_MAX_TASK_PRIORITY }, 0,
> +    &parse_int },
> +  { ENTRY ("OMP_ALLOCATOR"), { GOMP_ICV_ALLOCATOR }, 0, &parse_allocator },
> +  { ENTRY ("OMP_DEFAULT_DEVICE"), { GOMP_ICV_DEFAULT_DEVICE }, 0, &parse_int 
> }

All the entries in the table start with "OMP_" prefix, isn't it wasteful
to include that prefix in name field and name_len?
I mean, places that use the name to print it can just use OMP_%s and the
matching doesn't need to compare it again.

> +static bool
> +get_device_num (char *env, char *dev_num_ptr, int *dev_num, int *dev_num_len)
> +{
> +  char *end;
> +  int pos = 0;
> +
> +  if (dev_num_ptr[0] == '-')
> +    {
> +      gomp_error ("Non-negative device number expected in %s", env);
> +      return false;
> +    }
> +
> +  while (pos <= INT_MAX_STR_LEN)
> +    {
> +      if (dev_num_ptr[pos] == '\0' || dev_num_ptr[pos] == '=')
> +     break;
> +      pos++;
> +    }
> +  if (pos > INT_MAX_STR_LEN)
> +    {
> +      gomp_error ("Invalid device number in %s (too long)", env);
> +      return false;
> +    }

I don't understand the purpose of the above loop and if.

> +
> +  *dev_num = (int) strtoul (dev_num_ptr, &end, 10);

Just call strtoul and verify afterwards using end you get, no need to walk
it separately.
I wouldn't cast to int before verification that it fits though.
So perhaps
  char *end;
  unsigned long val = strtoul (dev_num_ptr, &end, 10);

  if (val > INT_MAX
      || *end != '='
      || (dev_num_ptr[0] == '0' && val != 0)
      || (dev_num_ptr[0] < '0' || dev_num_ptr[0] > '9'))
    {
      gomp_error ("Invalid device number in %s", env);
      return false;
    }
  *dev_num = val;
  *dev_num_len = end - dev_num_ptr;
  return true;
or so?  No need to differentiate different cases, and you want to
rule out "OMP_SCHEDULE_DEV_     \t+3=dynamic,3" too.

> +  if (dev_num_ptr[0] == '0' && *dev_num != 0)
> +    {
> +      gomp_error ("Invalid device number in %s (leading zero)", env);
> +      return false;
> +    }
> +  if (dev_num_ptr == end || *end != '=')
> +    {
> +      gomp_error ("Invalid device number in %s", env);
> +      return false;
> +    }
> +
> +  *dev_num_len = pos;
> +  return true;
> +}
> +

> +static uint32_t *
> +add_initial_icv_to_list (int dev_num, int icv_code, void *icv_addr[3])
> +{
> +  struct gomp_icv_list *last = NULL, *l = gomp_initial_icv_list;
> +  while (l != NULL && l->device_num != dev_num)
> +    {
> +      last = l;
> +      l = l->next;
> +    }
> +
> +  if (l == NULL)
> +    {
> +      l
> +     = (struct gomp_icv_list *) gomp_malloc (sizeof (struct gomp_icv_list));

What is the advantage of adding there a newline after the var name?
The = is indented 2 columns to the right from var name, so having it on one
line has the same length.

> +      l->device_num = dev_num;
> +      memset (&(l->icvs), 0, sizeof (struct gomp_initial_icvs));

You could use gomp_malloc_cleared (then format it as
      l = ((struct gomp_icv_list *)
           gomp_malloc_cleared (sizeof (struct gomp_icv_list)));

> +      l->flags = 0;
> +      if (dev_num < 0)
> +     {
> +       l->next = gomp_initial_icv_list;
> +       gomp_initial_icv_list = l;
> +     }
> +      else
> +     {
> +       l->next = NULL;
> +       if (last == NULL)
> +         gomp_initial_icv_list = l;
> +       else
> +         last->next = l;
> +     }
> +    }
> +
> +  get_icv_member_addr (&(l->icvs), icv_code, icv_addr);

No need for the ()s around l->icvs, &l->icvs will do (multiple places,
for other fields and other pointers too).
> +
> +  return &(l->flags);

Similarly.
> +
> +  /* Initial values for host environment variables should always exist even 
> if
> +     there is no explicitly set host environment variable.  Moreover, they 
> are
> +     set to the initial global values.  */
> +  add_initial_icv_to_list (-3, 0, NULL);
> +  none = gomp_get_initial_icv_item (-3);

Can you please add an enum for these -3/-2/-1 values and use it?

> +  none->icvs.nthreads_var = 1;
> +  none->icvs.thread_limit_var = UINT_MAX;
> +  none->icvs.run_sched_var = GFS_DYNAMIC;
> +  none->icvs.run_sched_chunk_size = 1;
> +  none->icvs.default_device_var = 0;
> +  none->icvs.dyn_var = false;
> +  none->icvs.max_active_levels_var = 1;
> +  none->icvs.bind_var = omp_proc_bind_false;

So, is this essentially a 3rd copy of the defaults?
gomp_global_icv has it, gomp_default_icv_values (partially) and
this spot too.  Don't we want to have it initialized once and copy over?
> +
> +  for (env = environ; *env != 0; env++)
> +    {
> +      if (!startswith (*env, "OMP_"))
> +     continue;
> +
> +     for (omp_var = 0; omp_var < OMP_VAR_CNT; omp_var++)
> +     {
> +       if (startswith (*env, envvars[omp_var].name))

As I said elsewhere,
        if (startswith (*env + sizeof ("OMP_") - 1, envvars[omp_var].name))
instead?
Or have a temporary var set to *env + sizeof ("OMP_") - 1; and use
it instead of *env.
The amount of &(*env)[ etc. spots that could be just &name[ is big.

> +         {
> +           pos = envvars[omp_var].name_len;
> +           if ((*env)[pos] == '=')
> +             {
> +               pos++;
> +               flag_var_addr
> +                 = add_initial_icv_to_list (-3,
> +                                            envvars[omp_var].flag_vars[0],
> +                                            params);
> +             }
> +           else if (startswith (&(*env)[pos], "_DEV=")
> +                    && envvars[omp_var].flag & GOMP_ENV_SUFFIX_DEV)
> +             {
> +               pos += 5;
> +               flag_var_addr
> +                 = add_initial_icv_to_list (-1,
> +                                            envvars[omp_var].flag_vars[0],
> +                                            params);
> +             }
> +           else if (startswith (&(*env)[pos], "_ALL=")
> +                    && envvars[omp_var].flag & GOMP_ENV_SUFFIX_ALL)
> +             {
> +               pos += 5;
> +               flag_var_addr
> +                 = add_initial_icv_to_list (-2,
> +                                            envvars[omp_var].flag_vars[0],
> +                                            params);
> +             }
> +           else if (startswith (&(*env)[pos], "_DEV_")
> +                    && envvars[omp_var].flag & GOMP_ENV_SUFFIX_DEV_X)
> +             {
> +               pos += 5;
> +               if (!get_device_num (*env, &(*env)[pos], &dev_num,
> +                                    &dev_num_len))
> +                 goto next_var;

Isn't goto next_var; equivalent to just break; ?

> +
> +               pos += dev_num_len + 1;
> +               flag_var_addr
> +                 = add_initial_icv_to_list (dev_num,
> +                                            envvars[omp_var].flag_vars[0],
> +                                            params);
> +             }
> +           else
> +             {
> +               gomp_error ("Invalid device number in %s", *env);
> +               break;
> +             }
> +           env_val = &(*env)[pos];
> +
> +           if (envvars[omp_var].parse_func (*env, env_val, params))
> +             {
> +               for (i = 0; i < 3; ++i)
> +                 if (envvars[omp_var].flag_vars[i])
> +                   gomp_set_icv_flag (flag_var_addr,
> +                                      envvars[omp_var].flag_vars[i]);
> +                 else
> +                   break;
> +             }
> +
> +           break;
> +         }
> +     }
> +
> + next_var:

So by using break; above we could drop this.

> +struct gomp_default_icv_t
> +{
> +  enum gomp_schedule_type run_sched_var;
> +  int run_sched_chunk_size;
> +  unsigned char max_active_levels_var;
> +  char bind_var;
> +  int nteams_var;
> +  int teams_thread_limit_var;
> +  int default_device_var;
> +};
> +extern struct gomp_default_icv_t gomp_default_icv_values;

Please don't mix the type definitions with extern declarations and
with function prototypes.
Function prototypes should go to the section of libgomp.h that starts
with /* Function prototypes.  */ comment and should go under section
corresponding to the filename that provides the definition.
For externs, there is a large block of extern declarations.

> + struct gomp_icv_list
> + {
> +   int device_num;
> +   struct gomp_initial_icvs icvs;
> +   uint32_t flags;

Put flags after device_num to avoid some useless padding?

> +   struct gomp_icv_list *next;
> + };

> +extern struct gomp_icv_list* gomp_add_device_specific_icv

* should go after space, not before.

> +int
> +main (int argc, char *const *argv)

No need for "int argc, char *const *argv" when you aren't using them,
just
int
main ()

> +{
> +  omp_display_env (1);
> +  omp_set_num_teams (24);
> +  if (omp_get_max_teams () != 24)
> +    abort ();
> +  omp_display_env (1);
> +
> +  return 0;
> +}
> +
> +/* { dg-output ".*\\\[host] OMP_NUM_TEAMS = '42'.*\\\[host] OMP_NUM_TEAMS = 
> '42'" } */

        Jakub

Reply via email to