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