diff options
author | Marcel Vollweiler <marcel@codesourcery.com> | 2022-09-08 10:01:33 -0700 |
---|---|---|
committer | Marcel Vollweiler <marcel@codesourcery.com> | 2022-09-08 10:19:37 -0700 |
commit | 9f2fca56593a2b87026b399d26adcdca90705685 (patch) | |
tree | 33fcfac4b0da2fd40ac628d809834461f01d986a /libgomp/env.c | |
parent | 338a5b0d7da84ef1f6c01dd96abf2c6bc830f403 (diff) | |
download | gcc-9f2fca56593a2b87026b399d26adcdca90705685.zip gcc-9f2fca56593a2b87026b399d26adcdca90705685.tar.gz gcc-9f2fca56593a2b87026b399d26adcdca90705685.tar.bz2 |
OpenMP, libgomp: Environment variable syntax extension
This patch considers the environment variable syntax extension for
device-specific variants of environment variables from OpenMP 5.1 (see
OpenMP 5.1 specification, p. 75 and p. 639). An environment variable (e.g.
OMP_NUM_TEAMS) can have different suffixes:
_DEV (e.g. OMP_NUM_TEAMS_DEV): affects all devices but not the host.
_DEV_<device> (e.g. OMP_NUM_TEAMS_DEV_42): affects only device with
number <device>.
no suffix (e.g. OMP_NUM_TEAMS): affects only the host.
In future OpenMP versions also suffix _ALL will be introduced (see discussion
https://github.com/OpenMP/spec/issues/3179). This is also considered in this
patch:
_ALL (e.g. OMP_NUM_TEAMS_ALL): affects all devices and the host.
The precedence is as follows (descending). For the host:
1. no suffix
2. _ALL
For devices:
1. _DEV_<device>
2. _DEV
3. _ALL
That means, _DEV_<device> is used whenever available. Otherwise _DEV is used if
available, and at last _ALL. If there is no value for any of the variable
variants, default values are used as already implemented before.
This patch concerns parsing (a), storing (b), output (c) and transmission to the
device (d):
(a) The actual number of devices and the numbering are not known when parsing
the environment variables. Thus all environment variables are iterated and
searched for device-specific ones.
(b) Only configured device-specific variables are stored. Thus, a linked list
is used.
(c) The output is done in omp_display_env (see specification p. 468f). Global
ICVs are tagged with [all], see https://github.com/OpenMP/spec/issues/3179.
ICVs which are not global but aren't handled device-specific yet are tagged
with [host]. omp_display_env outputs the initial values of the ICVs. That is
why a dedicated data structure is introduced for the inital values only
(gomp_initial_icv_list).
(d) Device-specific ICVs are transmitted to the device via GOMP_ADDITIONAL_ICVS.
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_values): 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.
(initialize_icvs): New function to initialize a gomp_initial_icvs
struct.
(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_device_num): Definition of device numbers for _ALL, _DEV, and
no suffix.
(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):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.
(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_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.
* libgomp.texi: Updated.
* 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 (gomp_get_offload_icv_item): Get a list item of
gomp_offload_icv_list.
(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.
Diffstat (limited to 'libgomp/env.c')
-rw-r--r-- | libgomp/env.c | 1320 |
1 files changed, 1019 insertions, 301 deletions
diff --git a/libgomp/env.c b/libgomp/env.c index 1c4ee89..82e22ac 100644 --- a/libgomp/env.c +++ b/libgomp/env.c @@ -61,18 +61,40 @@ #include "secure_getenv.h" -struct gomp_task_icv gomp_global_icv = { +/* Default values of ICVs according to the OpenMP standard. */ +const struct gomp_default_icv gomp_default_icv_values = { .nthreads_var = 1, .thread_limit_var = UINT_MAX, .run_sched_var = GFS_DYNAMIC, .run_sched_chunk_size = 1, .default_device_var = 0, - .dyn_var = false, .max_active_levels_var = 1, .bind_var = omp_proc_bind_false, + .nteams_var = 0, + .teams_thread_limit_var = 0, + .dyn_var = false +}; + +struct gomp_task_icv gomp_global_icv = { + .nthreads_var = gomp_default_icv_values.nthreads_var, + .thread_limit_var = gomp_default_icv_values.thread_limit_var, + .run_sched_var = gomp_default_icv_values.run_sched_var, + .run_sched_chunk_size = gomp_default_icv_values.run_sched_chunk_size, + .default_device_var = gomp_default_icv_values.default_device_var, + .dyn_var = gomp_default_icv_values.dyn_var, + .max_active_levels_var = gomp_default_icv_values.max_active_levels_var, + .bind_var = gomp_default_icv_values.bind_var, .target_data = NULL }; +/* List for initial "_DEV", "_ALL", and "_DEV_X" ICVs like OMP_NUM_TEAMS_DEV, + OMP_NUM_TEAMS_ALL, or OMP_NUM_TEAMS_DEV_42. */ +struct gomp_icv_list *gomp_initial_icv_list = NULL; + +/* List for "_DEV_X" ICVs like OMP_NUM_TEAMS_DEV_42. This list contains all + device-specific ICVs that are copied from host to device and back. */ +struct gomp_offload_icv_list *gomp_offload_icv_list = NULL; + bool gomp_cancel_var = false; enum gomp_target_offload_t gomp_target_offload_var = GOMP_TARGET_OFFLOAD_DEFAULT; @@ -104,86 +126,92 @@ 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; + gomp_error ("Invalid value for environment variable %.*s: %s", + (int) (val - env - 1), env, val); +} + +/* Parse the OMP_SCHEDULE environment variable. */ +static bool +parse_schedule (const char *env, const char *val, void *const params[]) +{ + enum gomp_schedule_type *schedule = (enum gomp_schedule_type *) params[0]; + int *chunk_size = (int *) params[1]; + char *end; unsigned long value; int monotonic = 0; - env = getenv ("OMP_SCHEDULE"); - if (env == NULL) - return; + if (val == NULL) + return false; - while (isspace ((unsigned char) *env)) - ++env; - if (strncasecmp (env, "monotonic", 9) == 0) + while (isspace ((unsigned char) *val)) + ++val; + if (strncasecmp (val, "monotonic", 9) == 0) { monotonic = 1; - env += 9; + val += 9; } - else if (strncasecmp (env, "nonmonotonic", 12) == 0) + else if (strncasecmp (val, "nonmonotonic", 12) == 0) { monotonic = -1; - env += 12; + val += 12; } if (monotonic) { - while (isspace ((unsigned char) *env)) - ++env; - if (*env != ':') + while (isspace ((unsigned char) *val)) + ++val; + if (*val != ':') goto unknown; - ++env; - while (isspace ((unsigned char) *env)) - ++env; + ++val; + while (isspace ((unsigned char) *val)) + ++val; } - if (strncasecmp (env, "static", 6) == 0) + if (strncasecmp (val, "static", 6) == 0) { - gomp_global_icv.run_sched_var = GFS_STATIC; - env += 6; + *schedule = GFS_STATIC; + val += 6; } - else if (strncasecmp (env, "dynamic", 7) == 0) + else if (strncasecmp (val, "dynamic", 7) == 0) { - gomp_global_icv.run_sched_var = GFS_DYNAMIC; - env += 7; + *schedule = GFS_DYNAMIC; + val += 7; } - else if (strncasecmp (env, "guided", 6) == 0) + else if (strncasecmp (val, "guided", 6) == 0) { - gomp_global_icv.run_sched_var = GFS_GUIDED; - env += 6; + *schedule = GFS_GUIDED; + val += 6; } - else if (strncasecmp (env, "auto", 4) == 0) + else if (strncasecmp (val, "auto", 4) == 0) { - gomp_global_icv.run_sched_var = GFS_AUTO; - env += 4; + *schedule = GFS_AUTO; + val += 4; } else goto unknown; if (monotonic == 1 - || (monotonic == 0 && gomp_global_icv.run_sched_var == GFS_STATIC)) - gomp_global_icv.run_sched_var |= GFS_MONOTONIC; + || (monotonic == 0 && *schedule == GFS_STATIC)) + *schedule |= GFS_MONOTONIC; - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') { - gomp_global_icv.run_sched_chunk_size - = (gomp_global_icv.run_sched_var & ~GFS_MONOTONIC) != GFS_STATIC; - return; + *chunk_size = (*schedule & ~GFS_MONOTONIC) != GFS_STATIC; + return true; } - if (*env++ != ',') + if (*val++ != ',') goto unknown; - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') goto invalid; errno = 0; - value = strtoul (env, &end, 10); - if (errno || end == env) + value = strtoul (val, &end, 10); + if (errno || end == val) goto invalid; while (isspace ((unsigned char) *end)) @@ -194,20 +222,22 @@ parse_schedule (void) if ((int)value != value) goto invalid; - if (value == 0 - && (gomp_global_icv.run_sched_var & ~GFS_MONOTONIC) != GFS_STATIC) + if (value == 0 && (*schedule & ~GFS_MONOTONIC) != GFS_STATIC) value = 1; - gomp_global_icv.run_sched_chunk_size = value; - return; + *chunk_size = value; + return true; unknown: - gomp_error ("Unknown value for environment variable OMP_SCHEDULE"); - return; + print_env_var_error (env, val); + return false; invalid: + char name[val - env]; + memcpy (name, env, val - env - 1); + name[val - env - 1] = '\0'; gomp_error ("Invalid value for chunk size in " - "environment variable OMP_SCHEDULE"); - return; + "environment variable %s: %s", name, val); + return false; } /* Parse an unsigned long environment variable. Return true if one was @@ -215,24 +245,23 @@ parse_schedule (void) environment variable. */ static bool -parse_unsigned_long_1 (const char *name, unsigned long *pvalue, bool allow_zero, - bool secure) +parse_unsigned_long_1 (const char *env, const char *val, unsigned long *pvalue, + bool allow_zero) { - char *env, *end; + char *end; unsigned long value; - env = (secure ? secure_getenv (name) : getenv (name)); - if (env == NULL) + if (val == NULL) return false; - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') goto invalid; errno = 0; - value = strtoul (env, &end, 10); - if (errno || end == env || (long) value <= 0 - allow_zero) + value = strtoul (val, &end, 10); + if (errno || end == val || (long) value <= 0 - allow_zero) goto invalid; while (isspace ((unsigned char) *end)) @@ -244,16 +273,36 @@ parse_unsigned_long_1 (const char *name, unsigned long *pvalue, bool allow_zero, return true; invalid: - gomp_error ("Invalid value for environment variable %s", name); + print_env_var_error (env, val); return false; } /* As parse_unsigned_long_1, but always use getenv. */ static bool -parse_unsigned_long (const char *name, unsigned long *pvalue, bool allow_zero) +parse_unsigned_long (const char *env, const char *val, void *const params[]) { - return parse_unsigned_long_1 (name, pvalue, allow_zero, false); + unsigned upper = (unsigned long) params[2]; + unsigned long pvalue = 0; + bool ret = parse_unsigned_long_1 (env, val, &pvalue, (bool) params[1]); + if (!ret) + return false; + + if (upper == 0) + *(unsigned long *) params[0] = pvalue; + else + { + if (pvalue > upper) + pvalue = upper; + if (upper <= UCHAR_MAX) + *(unsigned char *) params[0] = pvalue; + else if (upper <= UINT_MAX) + *(unsigned int *) params[0] = pvalue; + else + *(unsigned long *) params[0] = pvalue; + } + + return ret; } /* Parse a positive int environment variable. Return true if one was @@ -261,58 +310,57 @@ parse_unsigned_long (const char *name, unsigned long *pvalue, bool allow_zero) environment variable. */ static bool -parse_int_1 (const char *name, int *pvalue, bool allow_zero, bool secure) +parse_int_1 (const char *env, const char *val, int *pvalue, bool allow_zero) { unsigned long value; - if (!parse_unsigned_long_1 (name, &value, allow_zero, secure)) + if (!parse_unsigned_long_1 (env, val, &value, allow_zero)) return false; if (value > INT_MAX) { - gomp_error ("Invalid value for environment variable %s", name); + print_env_var_error (env, val); return false; } *pvalue = (int) value; return true; } -/* As parse_int_1, but use getenv. */ - static bool -parse_int (const char *name, int *pvalue, bool allow_zero) +parse_int (const char *env, const char *val, void *const params[]) { - return parse_int_1 (name, pvalue, allow_zero, false); + return parse_int_1 (env, val, (int *) params[0], (bool) params[1]); } /* As parse_int_1, but use getenv_secure. */ static bool -parse_int_secure (const char *name, int *pvalue, bool allow_zero) +parse_int_secure (const char *env, int *pvalue, bool allow_zero) { - return parse_int_1 (name, pvalue, allow_zero, true); + return parse_int_1 (env, secure_getenv (env), pvalue, allow_zero); } /* Parse an unsigned long list environment variable. Return true if one was present and it was successfully parsed. */ static bool -parse_unsigned_long_list (const char *name, unsigned long *p1stvalue, - unsigned long **pvalues, - unsigned long *pnvalues) +parse_unsigned_long_list (const char *env, const char *val, + void *const params[]) { - char *env, *end; + unsigned long *p1stvalue = (unsigned long *) params[0]; + unsigned long **pvalues = (unsigned long **) params[1]; + unsigned long *pnvalues = (unsigned long *) params[2]; + char *end; unsigned long value, *values = NULL; - env = getenv (name); - if (env == NULL) + if (val == NULL) return false; - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') goto invalid; errno = 0; - value = strtoul (env, &end, 10); + value = strtoul (val, &end, 10); if (errno || (long) value <= 0) goto invalid; @@ -326,7 +374,7 @@ parse_unsigned_long_list (const char *name, unsigned long *p1stvalue, do { - env = end + 1; + val = end + 1; if (nvalues == nalloced) { unsigned long *n; @@ -335,6 +383,9 @@ parse_unsigned_long_list (const char *name, unsigned long *p1stvalue, if (n == NULL) { free (values); + char name[val - env]; + memcpy (name, env, val - env - 1); + name[val - env - 1] = '\0'; gomp_error ("Out of memory while trying to parse" " environment variable %s", name); return false; @@ -344,13 +395,13 @@ parse_unsigned_long_list (const char *name, unsigned long *p1stvalue, values[nvalues++] = value; } - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') goto invalid; errno = 0; - value = strtoul (env, &end, 10); + value = strtoul (val, &end, 10); if (errno || (long) value <= 0) goto invalid; @@ -370,52 +421,56 @@ parse_unsigned_long_list (const char *name, unsigned long *p1stvalue, } goto invalid; } + else + { + *pnvalues = 0; + *pvalues = NULL; + } *p1stvalue = value; return true; invalid: free (values); - gomp_error ("Invalid value for environment variable %s", name); + print_env_var_error (env, val); return false; } -static void -parse_target_offload (const char *name, enum gomp_target_offload_t *offload) +static bool +parse_target_offload (const char *env, const char *val, void *const params[]) { - const char *env; int new_offload = -1; - env = getenv (name); - if (env == NULL) - return; + if (val == NULL) + return false; - while (isspace ((unsigned char) *env)) - ++env; - if (strncasecmp (env, "default", 7) == 0) + while (isspace ((unsigned char) *val)) + ++val; + if (strncasecmp (val, "default", 7) == 0) { - env += 7; + val += 7; new_offload = GOMP_TARGET_OFFLOAD_DEFAULT; } - else if (strncasecmp (env, "mandatory", 9) == 0) + else if (strncasecmp (val, "mandatory", 9) == 0) { - env += 9; + val += 9; new_offload = GOMP_TARGET_OFFLOAD_MANDATORY; } - else if (strncasecmp (env, "disabled", 8) == 0) + else if (strncasecmp (val, "disabled", 8) == 0) { - env += 8; + val += 8; new_offload = GOMP_TARGET_OFFLOAD_DISABLED; } - while (isspace ((unsigned char) *env)) - ++env; - if (new_offload != -1 && *env == '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (new_offload != -1 && *val == '\0') { - *offload = new_offload; - return; + *(enum gomp_target_offload_t *) params[0] = new_offload; + return true; } - gomp_error ("Invalid value for environment variable OMP_TARGET_OFFLOAD"); + print_env_var_error (env, val); + return false; } /* Parse environment variable set to a boolean or list of omp_proc_bind_t @@ -423,10 +478,11 @@ parse_target_offload (const char *name, enum gomp_target_offload_t *offload) parsed. */ static bool -parse_bind_var (const char *name, char *p1stvalue, - char **pvalues, unsigned long *pnvalues) +parse_bind_var (const char *env, const char *val, void *const params[]) { - char *env; + char *p1stvalue = (char *) params[0]; + char **pvalues = (char **) params[1]; + unsigned long *pnvalues = (unsigned long *) params[2]; char value = omp_proc_bind_false, *values = NULL; int i; static struct proc_bind_kinds @@ -444,30 +500,29 @@ parse_bind_var (const char *name, char *p1stvalue, { "spread", 6, omp_proc_bind_spread } }; - env = getenv (name); - if (env == NULL) + if (val == NULL) return false; - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') goto invalid; for (i = 0; i < 6; i++) - if (strncasecmp (env, kinds[i].name, kinds[i].len) == 0) + if (strncasecmp (val, kinds[i].name, kinds[i].len) == 0) { value = kinds[i].kind; - env += kinds[i].len; + val += kinds[i].len; break; } if (i == 6) goto invalid; - while (isspace ((unsigned char) *env)) - ++env; - if (*env != '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (*val != '\0') { - if (*env == ',') + if (*val == ',') { unsigned long nvalues = 0, nalloced = 0; @@ -477,7 +532,7 @@ parse_bind_var (const char *name, char *p1stvalue, do { - env++; + val++; if (nvalues == nalloced) { char *n; @@ -486,6 +541,9 @@ parse_bind_var (const char *name, char *p1stvalue, if (n == NULL) { free (values); + char name[val - env]; + memcpy (name, env, val - env - 1); + name[val - env - 1] = '\0'; gomp_error ("Out of memory while trying to parse" " environment variable %s", name); return false; @@ -495,27 +553,27 @@ parse_bind_var (const char *name, char *p1stvalue, values[nvalues++] = value; } - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') goto invalid; for (i = 2; i < 6; i++) - if (strncasecmp (env, kinds[i].name, kinds[i].len) == 0) + if (strncasecmp (val, kinds[i].name, kinds[i].len) == 0) { value = kinds[i].kind; - env += kinds[i].len; + val += kinds[i].len; break; } if (i == 6) goto invalid; values[nvalues++] = value; - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') break; - if (*env != ',') + if (*val != ',') goto invalid; } while (1); @@ -532,7 +590,7 @@ parse_bind_var (const char *name, char *p1stvalue, invalid: free (values); - gomp_error ("Invalid value for environment variable %s", name); + print_env_var_error (env, val); return false; } @@ -865,23 +923,22 @@ parse_places_var (const char *name, bool ignore) present and it was successfully parsed. */ static bool -parse_stacksize (const char *name, unsigned long *pvalue) +parse_stacksize (const char *env, const char *val, void *const params[]) { - char *env, *end; + char *end; unsigned long value, shift = 10; - env = getenv (name); - if (env == NULL) + if (val == NULL) return false; - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') goto invalid; errno = 0; - value = strtoul (env, &end, 10); - if (errno || end == env) + value = strtoul (val, &end, 10); + if (errno || end == val) goto invalid; while (isspace ((unsigned char) *end)) @@ -914,11 +971,11 @@ parse_stacksize (const char *name, unsigned long *pvalue) if (((value << shift) >> shift) != value) goto invalid; - *pvalue = value << shift; + *(unsigned long *) params[0] = value << shift; return true; invalid: - gomp_error ("Invalid value for environment variable %s", name); + print_env_var_error (env, val); return false; } @@ -998,35 +1055,33 @@ parse_spincount (const char *name, unsigned long long *pvalue) /* Parse a boolean value for environment variable NAME and store the result in VALUE. Return true if one was present and it was successfully parsed. */ - static bool -parse_boolean (const char *name, bool *value) +parse_boolean (const char *env, const char *val, void *const params[]) { - const char *env; + bool *value = (bool *) params[0]; - env = getenv (name); - if (env == NULL) + if (val == NULL) return false; - while (isspace ((unsigned char) *env)) - ++env; - if (strncasecmp (env, "true", 4) == 0) + while (isspace ((unsigned char) *val)) + ++val; + if (strncasecmp (val, "true", 4) == 0) { *value = true; - env += 4; + val += 4; } - else if (strncasecmp (env, "false", 5) == 0) + else if (strncasecmp (val, "false", 5) == 0) { *value = false; - env += 5; + val += 5; } else - env = "X"; - while (isspace ((unsigned char) *env)) - ++env; - if (*env != '\0') + val = "X"; + while (isspace ((unsigned char) *val)) + ++val; + if (*val != '\0') { - gomp_error ("Invalid value for environment variable %s", name); + print_env_var_error (env, val); return false; } return true; @@ -1034,36 +1089,42 @@ parse_boolean (const char *name, bool *value) /* Parse the OMP_WAIT_POLICY environment variable and return the value. */ -static int -parse_wait_policy (void) +static bool +parse_wait_policy (const char *env, const char *val, void *const params[]) { - const char *env; + int *pvalue = (int *) params[0]; int ret = -1; - env = getenv ("OMP_WAIT_POLICY"); - if (env == NULL) - return -1; + if (val == NULL) + { + *pvalue = -1; + return false; + } - while (isspace ((unsigned char) *env)) - ++env; - if (strncasecmp (env, "active", 6) == 0) + while (isspace ((unsigned char) *val)) + ++val; + if (strncasecmp (val, "active", 6) == 0) { ret = 1; - env += 6; + val += 6; } - else if (strncasecmp (env, "passive", 7) == 0) + else if (strncasecmp (val, "passive", 7) == 0) { ret = 0; - env += 7; + val += 7; } else - env = "X"; - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') - return ret; - gomp_error ("Invalid value for environment variable OMP_WAIT_POLICY"); - return -1; + val = "X"; + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') + { + *pvalue = ret; + return true; + } + print_env_var_error (env, val); + *pvalue = -1; + return false; } /* Parse the GOMP_CPU_AFFINITY environment varible. Return true if one was @@ -1167,26 +1228,24 @@ parse_affinity (bool ignore) } /* Parse the OMP_ALLOCATOR environment variable and return the value. */ - -static uintptr_t -parse_allocator (void) +static bool +parse_allocator (const char *env, const char *val, void *const params[]) { - const char *env; - uintptr_t ret = omp_default_mem_alloc; + uintptr_t *ret = (uintptr_t *) params[0]; + *ret = omp_default_mem_alloc; - env = getenv ("OMP_ALLOCATOR"); - if (env == NULL) - return ret; + if (val == NULL) + return false; - while (isspace ((unsigned char) *env)) - ++env; + while (isspace ((unsigned char) *val)) + ++val; if (0) ; #define C(v) \ - else if (strncasecmp (env, #v, sizeof (#v) - 1) == 0) \ + else if (strncasecmp (val, #v, sizeof (#v) - 1) == 0) \ { \ - ret = v; \ - env += sizeof (#v) - 1; \ + *ret = v; \ + val += sizeof (#v) - 1; \ } C (omp_default_mem_alloc) C (omp_large_cap_mem_alloc) @@ -1198,13 +1257,14 @@ parse_allocator (void) C (omp_thread_mem_alloc) #undef C else - env = "X"; - while (isspace ((unsigned char) *env)) - ++env; - if (*env == '\0') - return ret; - gomp_error ("Invalid value for environment variable OMP_ALLOCATOR"); - return omp_default_mem_alloc; + val = "X"; + while (isspace ((unsigned char) *val)) + ++val; + if (*val == '\0') + return true; + print_env_var_error (env, val); + *ret = omp_default_mem_alloc; + return false; } static void @@ -1251,62 +1311,59 @@ parse_gomp_openacc_dim (void) } } -void -omp_display_env (int verbose) -{ - int i; - - fputs ("\nOPENMP DISPLAY ENVIRONMENT BEGIN\n", stderr); +/* Helper function for omp_display_env which prints the values of run_sched_var. + 'device' can be 'host', 'dev', 'all' or a particular device number. */ - fputs (" _OPENMP = '201511'\n", stderr); - fprintf (stderr, " OMP_DYNAMIC = '%s'\n", - gomp_global_icv.dyn_var ? "TRUE" : "FALSE"); - fprintf (stderr, " OMP_NESTED = '%s'\n", - gomp_global_icv.max_active_levels_var > 1 ? "TRUE" : "FALSE"); - - fprintf (stderr, " OMP_NUM_THREADS = '%lu", gomp_global_icv.nthreads_var); - for (i = 1; i < gomp_nthreads_var_list_len; i++) - fprintf (stderr, ",%lu", gomp_nthreads_var_list[i]); - fputs ("'\n", stderr); - - fprintf (stderr, " OMP_SCHEDULE = '"); - if ((gomp_global_icv.run_sched_var & GFS_MONOTONIC)) +static void +print_schedule (enum gomp_schedule_type run_sched_var, int run_sched_chunk_size, + const char *device) +{ + fprintf (stderr, " [%s] OMP_SCHEDULE = '", device); + if ((run_sched_var & GFS_MONOTONIC)) { - if (gomp_global_icv.run_sched_var != (GFS_MONOTONIC | GFS_STATIC)) + if (run_sched_var != (GFS_MONOTONIC | GFS_STATIC)) fputs ("MONOTONIC:", stderr); } - else if (gomp_global_icv.run_sched_var == GFS_STATIC) + else if (run_sched_var == GFS_STATIC) fputs ("NONMONOTONIC:", stderr); - switch (gomp_global_icv.run_sched_var & ~GFS_MONOTONIC) + switch (run_sched_var & ~GFS_MONOTONIC) { case GFS_RUNTIME: fputs ("RUNTIME", stderr); - if (gomp_global_icv.run_sched_chunk_size != 1) - fprintf (stderr, ",%d", gomp_global_icv.run_sched_chunk_size); + if (run_sched_chunk_size != 1) + fprintf (stderr, ",%d", run_sched_chunk_size); break; case GFS_STATIC: fputs ("STATIC", stderr); - if (gomp_global_icv.run_sched_chunk_size != 0) - fprintf (stderr, ",%d", gomp_global_icv.run_sched_chunk_size); + if (run_sched_chunk_size != 0) + fprintf (stderr, ",%d", run_sched_chunk_size); break; case GFS_DYNAMIC: fputs ("DYNAMIC", stderr); - if (gomp_global_icv.run_sched_chunk_size != 1) - fprintf (stderr, ",%d", gomp_global_icv.run_sched_chunk_size); + if (run_sched_chunk_size != 1) + fprintf (stderr, ",%d", run_sched_chunk_size); break; case GFS_GUIDED: fputs ("GUIDED", stderr); - if (gomp_global_icv.run_sched_chunk_size != 1) - fprintf (stderr, ",%d", gomp_global_icv.run_sched_chunk_size); + if (run_sched_chunk_size != 1) + fprintf (stderr, ",%d", run_sched_chunk_size); break; case GFS_AUTO: fputs ("AUTO", stderr); break; } fputs ("'\n", stderr); +} + +/* Helper function for omp_display_env which prints the values of proc_bind_var. + 'device' can be 'host', 'dev', 'all', or a particular device number. */ - fputs (" OMP_PROC_BIND = '", stderr); - switch (gomp_global_icv.bind_var) +static void +print_proc_bind (char proc_bind_var, unsigned long len, char **list, + const char *device) +{ + fprintf (stderr, " [%s] OMP_PROC_BIND = '", device); + switch (proc_bind_var) { case omp_proc_bind_false: fputs ("FALSE", stderr); @@ -1324,8 +1381,8 @@ omp_display_env (int verbose) fputs ("SPREAD", stderr); break; } - for (i = 1; i < gomp_bind_var_list_len; i++) - switch (gomp_bind_var_list[i]) + for (int i = 1; i < len; i++) + switch ((*list)[i]) { case omp_proc_bind_master: fputs (",MASTER", stderr); /* TODO: Change to PRIMARY for OpenMP 5.1. */ @@ -1338,7 +1395,290 @@ omp_display_env (int verbose) break; } fputs ("'\n", stderr); - fputs (" OMP_PLACES = '", stderr); +} + +enum gomp_parse_type +{ + PARSE_INT = 1, + PARSE_BOOL = 2, + PARSE_UINT = 3, + PARSE_ULONG = 4, + PARSE_UCHAR = 5, + PARSE_SCHEDULE =6, + PARSE_BIND = 7 +}; + +/* The following table contains items that help parsing environment variables + and fill corresponding ICVs with values. FLAG_VARS contain all ICVS which + are affected by the environment variable. FLAGS determine what variant of + environment variable is allowed. */ + +#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 ("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 ("NUM_TEAMS"), + { GOMP_ICV_NTEAMS }, + GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X, + &parse_int }, + { ENTRY ("DYNAMIC"), + { GOMP_ICV_DYNAMIC }, + GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X, + &parse_boolean }, + { ENTRY ("TEAMS_THREAD_LIMIT"), + { GOMP_ICV_TEAMS_THREAD_LIMIT }, + GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X, + &parse_int }, + { ENTRY ("THREAD_LIMIT"), + { GOMP_ICV_THREAD_LIMIT }, + GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X, + &parse_unsigned_long }, + { ENTRY ("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 ("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 ("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 ("WAIT_POLICY"), + { GOMP_ICV_WAIT_POLICY }, + GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X, + &parse_wait_policy }, + { ENTRY ("STACKSIZE"), + { GOMP_ICV_STACKSIZE }, + GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X, + &parse_stacksize }, + { ENTRY ("CANCELLATION"), { GOMP_ICV_CANCELLATION }, 0, &parse_boolean }, + { ENTRY ("DISPLAY_AFFINITY"), { GOMP_ICV_DISPLAY_AFFINITY }, 0, + &parse_boolean }, + { ENTRY ("TARGET_OFFLOAD"), { GOMP_ICV_TARGET_OFFLOAD }, 0, + &parse_target_offload }, + { ENTRY ("MAX_TASK_PRIORITY"), { GOMP_ICV_MAX_TASK_PRIORITY }, 0, + &parse_int }, + { ENTRY ("ALLOCATOR"), { GOMP_ICV_ALLOCATOR }, 0, &parse_allocator }, + { ENTRY ("DEFAULT_DEVICE"), { GOMP_ICV_DEFAULT_DEVICE }, + GOMP_ENV_SUFFIX_DEV | GOMP_ENV_SUFFIX_ALL | GOMP_ENV_SUFFIX_DEV_X, + &parse_int } +}; +#undef ENTRY +#define OMP_VAR_CNT (sizeof (envvars) / sizeof (envvars[0])) + +/* The following table is used to apply the hierarchy of ICV variants for host + variables, e.g. nteams_var is set to OMP_NUM_TEAMS_ALL if OMP_NUM_TEAMS is + undefined. */ + +static const struct host_envvar +{ + unsigned char flag_var; + void *dest[3]; + int type_code; +} host_envvars[] = { + { GOMP_ICV_NTEAMS, { &gomp_nteams_var }, PARSE_INT }, + { GOMP_ICV_DYNAMIC, { &gomp_global_icv.dyn_var }, PARSE_BOOL }, + { GOMP_ICV_DEFAULT_DEVICE, { &gomp_global_icv.default_device_var }, + PARSE_INT }, + { GOMP_ICV_TEAMS_THREAD_LIMIT, { &gomp_teams_thread_limit_var }, PARSE_INT }, + { GOMP_ICV_SCHEDULE, + { &gomp_global_icv.run_sched_var, &gomp_global_icv.run_sched_chunk_size }, + PARSE_SCHEDULE }, + { GOMP_ICV_THREAD_LIMIT, { &gomp_global_icv.thread_limit_var }, PARSE_UINT }, + { GOMP_ICV_NTHREADS, + { &gomp_global_icv.nthreads_var, &gomp_nthreads_var_list, + &gomp_nthreads_var_list_len }, PARSE_ULONG }, + { GOMP_ICV_BIND, + { &gomp_global_icv.bind_var, &gomp_bind_var_list, &gomp_bind_var_list_len }, + PARSE_BIND }, + { GOMP_ICV_MAX_ACTIVE_LEVELS, { &gomp_global_icv.max_active_levels_var }, + PARSE_UCHAR }, +}; +#define OMP_HOST_VAR_CNT (sizeof (host_envvars) / sizeof (host_envvars[0])) + +#define INT_MAX_STR_LEN 10 + +bool +gomp_get_icv_flag (uint32_t value, enum gomp_icvs icv) +{ + return value & (1 << (icv - 1)); +} + +static void +gomp_set_icv_flag (uint32_t *value, enum gomp_icvs icv) +{ + *value |= 1 << (icv - 1); +} + +static void +print_device_specific_icvs (int icv_code) +{ + struct gomp_icv_list *list = gomp_initial_icv_list; + int i; + char dev_num[INT_MAX_STR_LEN + 1]; + + while (list != NULL) + { + if (list->device_num < 0) + { + list = list->next; + continue; + } + + switch (icv_code) + { + case GOMP_ICV_NTEAMS: + if (gomp_get_icv_flag (list->flags, GOMP_ICV_NTEAMS)) + fprintf (stderr, " [%d] OMP_NUM_TEAMS = '%d'\n", + list->device_num, list->icvs.nteams_var); + break; + case GOMP_ICV_DYNAMIC: + if (gomp_get_icv_flag (list->flags, GOMP_ICV_DYNAMIC)) + fprintf (stderr, " [%d] OMP_DYNAMIC = '%s'\n", + list->device_num, list->icvs.dyn_var ? "TRUE" : "FALSE"); + break; + case GOMP_ICV_TEAMS_THREAD_LIMIT: + if (gomp_get_icv_flag (list->flags, GOMP_ICV_TEAMS_THREAD_LIMIT)) + fprintf (stderr, " [%d] OMP_TEAMS_THREAD_LIMIT = '%u'\n", + list->device_num, list->icvs.teams_thread_limit_var); + break; + case GOMP_ICV_SCHEDULE: + if (!(gomp_get_icv_flag (list->flags, GOMP_ICV_SCHEDULE))) + break; + sprintf (dev_num, "%d", list->device_num); + print_schedule (list->icvs.run_sched_var, + list->icvs.run_sched_chunk_size, + dev_num); + break; + case GOMP_ICV_THREAD_LIMIT: + if (gomp_get_icv_flag (list->flags, GOMP_ICV_THREAD_LIMIT)) + fprintf (stderr, " [%d] OMP_THREAD_LIMIT = '%d'\n", + list->device_num, list->icvs.thread_limit_var); + break; + case GOMP_ICV_NTHREADS: + if (!(gomp_get_icv_flag (list->flags, GOMP_ICV_NTHREADS))) + break; + fprintf (stderr, " [%d] OMP_NUM_THREADS = '%lu", list->device_num, + list->icvs.nthreads_var); + for (i = 1; i < list->icvs.nthreads_var_list_len; i++) + fprintf (stderr, ",%lu", list->icvs.nthreads_var_list[i]); + fputs ("'\n", stderr); + break; + case GOMP_ICV_MAX_ACTIVE_LEVELS: + fprintf (stderr, " [%d] OMP_MAX_ACTIVE_LEVELS = '%u'\n", + list->device_num, list->icvs.max_active_levels_var); + break; + case GOMP_ICV_BIND: + if (!(gomp_get_icv_flag (list->flags, GOMP_ICV_BIND))) + break; + sprintf (dev_num, "%d", list->device_num); + print_proc_bind (list->icvs.bind_var, list->icvs.bind_var_list_len, + &list->icvs.bind_var_list, dev_num); + break; + case GOMP_ICV_WAIT_POLICY: + if (gomp_get_icv_flag (list->flags, GOMP_ICV_WAIT_POLICY)) + fprintf (stderr, " [%d] OMP_WAIT_POLICY = '%s'\n", + list->device_num, + list->icvs.wait_policy > 0 ? "ACTIVE" : "PASSIVE"); + break; + case GOMP_ICV_STACKSIZE: + if (gomp_get_icv_flag (list->flags, GOMP_ICV_STACKSIZE)) + fprintf (stderr, " [%d] OMP_STACKSIZE = '%lu'\n", + list->device_num, list->icvs.stacksize); + break; + } + list = list->next; + } +} + +void +omp_display_env (int verbose) +{ + int i; + struct gomp_icv_list *dev + = gomp_get_initial_icv_item (GOMP_DEVICE_NUM_FOR_DEV); + struct gomp_icv_list *all + = gomp_get_initial_icv_item (GOMP_DEVICE_NUM_FOR_ALL); + struct gomp_icv_list *none + = gomp_get_initial_icv_item (GOMP_DEVICE_NUM_FOR_NO_SUFFIX); + + fputs ("\nOPENMP DISPLAY ENVIRONMENT BEGIN\n", stderr); + + fputs (" _OPENMP = '201511'\n", stderr); + + fprintf (stderr, " [host] OMP_DYNAMIC = '%s'\n", + none->icvs.dyn_var ? "TRUE" : "FALSE"); + if (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_DYNAMIC)) + fprintf (stderr, " [all] OMP_DYNAMIC = '%s'\n", + all->icvs.dyn_var ? "TRUE" : "FALSE"); + if (dev != NULL && gomp_get_icv_flag (dev->flags, GOMP_ICV_DYNAMIC)) + fprintf (stderr, " [device] OMP_DYNAMIC = '%s'\n", + dev->icvs.dyn_var ? "TRUE" : "FALSE"); + print_device_specific_icvs (GOMP_ICV_DYNAMIC); + + /* The OMP_NESTED environment variable has been deprecated. */ + fprintf (stderr, " [host] OMP_NESTED = '%s'\n", + none->icvs.max_active_levels_var > 1 ? "TRUE" : "FALSE"); + + fprintf (stderr, " [host] OMP_NUM_THREADS = '%lu", + none->icvs.nthreads_var); + for (i = 1; i < none->icvs.nthreads_var_list_len; i++) + fprintf (stderr, ",%lu", none->icvs.nthreads_var_list[i]); + fputs ("'\n", stderr); + if (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_NTHREADS)) + { + fprintf (stderr, " [all] OMP_NUM_THREADS = '%lu", + all->icvs.nthreads_var); + for (i = 1; i < all->icvs.nthreads_var_list_len; i++) + fprintf (stderr, ",%lu", all->icvs.nthreads_var_list[i]); + fputs ("'\n", stderr); + } + if (dev != NULL && gomp_get_icv_flag (dev->flags, GOMP_ICV_NTHREADS)) + { + fprintf (stderr, " [device] OMP_NUM_THREADS = '%lu", + dev->icvs.nthreads_var); + for (i = 1; i < dev->icvs.nthreads_var_list_len; i++) + fprintf (stderr, ",%lu", dev->icvs.nthreads_var_list[i]); + fputs ("'\n", stderr); + } + print_device_specific_icvs (GOMP_ICV_NTHREADS); + + + print_schedule (none->icvs.run_sched_var, + none->icvs.run_sched_chunk_size, "host"); + if (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_SCHEDULE)) + print_schedule (all->icvs.run_sched_var, + all->icvs.run_sched_chunk_size, "all"); + if (dev != NULL && gomp_get_icv_flag (dev->flags, GOMP_ICV_SCHEDULE)) + print_schedule (dev->icvs.run_sched_var, + dev->icvs.run_sched_chunk_size, "device"); + print_device_specific_icvs (GOMP_ICV_SCHEDULE); + + print_proc_bind (none->icvs.bind_var, + none->icvs.bind_var_list_len, + &none->icvs.bind_var_list, "host"); + if (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_BIND)) + print_proc_bind (all->icvs.bind_var, + all->icvs.bind_var_list_len, + &all->icvs.bind_var_list, "all"); + if (dev != NULL && gomp_get_icv_flag (dev->flags, GOMP_ICV_BIND)) + print_proc_bind (dev->icvs.bind_var, + dev->icvs.bind_var_list_len, + &dev->icvs.bind_var_list, "device"); + print_device_specific_icvs (GOMP_ICV_BIND); + + fputs (" [host] OMP_PLACES = '", stderr); for (i = 0; i < gomp_places_list_len; i++) { fputs ("{", stderr); @@ -1347,30 +1687,85 @@ omp_display_env (int verbose) } fputs ("'\n", stderr); - fprintf (stderr, " OMP_STACKSIZE = '%lu'\n", stacksize); + fprintf (stderr, " [host] OMP_STACKSIZE = '%lu'\n", + none->icvs.stacksize); + if (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_STACKSIZE)) + fprintf (stderr, " [all] OMP_STACKSIZE = '%lu'\n", + all->icvs.stacksize); + if (dev != NULL && gomp_get_icv_flag (dev->flags, GOMP_ICV_STACKSIZE)) + fprintf (stderr, " [device] OMP_STACKSIZE = '%lu'\n", + dev->icvs.stacksize); + print_device_specific_icvs (GOMP_ICV_STACKSIZE); /* GOMP's default value is actually neither active nor passive. */ - fprintf (stderr, " OMP_WAIT_POLICY = '%s'\n", - wait_policy > 0 ? "ACTIVE" : "PASSIVE"); - fprintf (stderr, " OMP_THREAD_LIMIT = '%u'\n", - gomp_global_icv.thread_limit_var); - fprintf (stderr, " OMP_MAX_ACTIVE_LEVELS = '%u'\n", - gomp_global_icv.max_active_levels_var); - fprintf (stderr, " OMP_NUM_TEAMS = '%u'\n", gomp_nteams_var); - fprintf (stderr, " OMP_TEAMS_THREAD_LIMIT = '%u'\n", - gomp_teams_thread_limit_var); - - fprintf (stderr, " OMP_CANCELLATION = '%s'\n", + fprintf (stderr, " [host] OMP_WAIT_POLICY = '%s'\n", + none->icvs.wait_policy > 0 ? "ACTIVE" : "PASSIVE"); + if (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_WAIT_POLICY)) + fprintf (stderr, " [all] OMP_WAIT_POLICY = '%s'\n", + all->icvs.wait_policy > 0 ? "ACTIVE" : "PASSIVE"); + if (dev != NULL && gomp_get_icv_flag (dev->flags, GOMP_ICV_WAIT_POLICY)) + fprintf (stderr, " [device] OMP_WAIT_POLICY = '%s'\n", + dev->icvs.wait_policy > 0 ? "ACTIVE" : "PASSIVE"); + print_device_specific_icvs (GOMP_ICV_WAIT_POLICY); + + fprintf (stderr, " [host] OMP_THREAD_LIMIT = '%u'\n", + none->icvs.thread_limit_var); + if (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_THREAD_LIMIT)) + fprintf (stderr, " [all] OMP_THREAD_LIMIT = '%d'\n", + all->icvs.thread_limit_var); + if (dev != NULL && gomp_get_icv_flag (dev->flags, GOMP_ICV_THREAD_LIMIT)) + fprintf (stderr, " [device] OMP_THREAD_LIMIT = '%d'\n", + dev->icvs.thread_limit_var); + print_device_specific_icvs (GOMP_ICV_THREAD_LIMIT); + + fprintf (stderr, " [host] OMP_MAX_ACTIVE_LEVELS = '%u'\n", + none->icvs.max_active_levels_var); + if (all != NULL && gomp_get_icv_flag (all->flags, + GOMP_ICV_MAX_ACTIVE_LEVELS)) + fprintf (stderr, " [all] OMP_MAX_ACTIVE_LEVELS = '%u'\n", + all->icvs.max_active_levels_var); + if (dev != NULL && gomp_get_icv_flag (dev->flags, + GOMP_ICV_MAX_ACTIVE_LEVELS)) + fprintf (stderr, " [device] OMP_MAX_ACTIVE_LEVELS = '%u'\n", + dev->icvs.max_active_levels_var); + print_device_specific_icvs (GOMP_ICV_MAX_ACTIVE_LEVELS); + + + fprintf (stderr, " [host] OMP_NUM_TEAMS = '%d'\n", + none->icvs.nteams_var); + if (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_NTEAMS)) + fprintf (stderr, " [all] OMP_NUM_TEAMS = '%d'\n", + all->icvs.nteams_var); + if (dev != NULL && gomp_get_icv_flag (dev->flags, GOMP_ICV_NTEAMS)) + fprintf (stderr, " [device] OMP_NUM_TEAMS = '%d'\n", + dev->icvs.nteams_var); + print_device_specific_icvs (GOMP_ICV_NTEAMS); + + fprintf (stderr, " [host] OMP_TEAMS_THREAD_LIMIT = '%u'\n", + none->icvs.teams_thread_limit_var); + if (all != NULL && gomp_get_icv_flag (all->flags, + GOMP_ICV_TEAMS_THREAD_LIMIT)) + fprintf (stderr, " [all] OMP_TEAMS_THREAD_LIMIT = '%u'\n", + all->icvs.teams_thread_limit_var); + if (dev != NULL && gomp_get_icv_flag (dev->flags, + GOMP_ICV_TEAMS_THREAD_LIMIT)) + fprintf (stderr, " [device] OMP_TEAMS_THREAD_LIMIT = '%u'\n", + dev->icvs.teams_thread_limit_var); + print_device_specific_icvs (GOMP_ICV_TEAMS_THREAD_LIMIT); + + fprintf (stderr, " [all] OMP_CANCELLATION = '%s'\n", gomp_cancel_var ? "TRUE" : "FALSE"); - fprintf (stderr, " OMP_DEFAULT_DEVICE = '%d'\n", - gomp_global_icv.default_device_var); - fprintf (stderr, " OMP_MAX_TASK_PRIORITY = '%d'\n", + + fprintf (stderr, " [all] OMP_DEFAULT_DEVICE = '%d'\n", + none->icvs.default_device_var); + + fprintf (stderr, " [all] OMP_MAX_TASK_PRIORITY = '%d'\n", gomp_max_task_priority_var); - fprintf (stderr, " OMP_DISPLAY_AFFINITY = '%s'\n", + fprintf (stderr, " [all] OMP_DISPLAY_AFFINITY = '%s'\n", gomp_display_affinity_var ? "TRUE" : "FALSE"); - fprintf (stderr, " OMP_AFFINITY_FORMAT = '%s'\n", + fprintf (stderr, " [host] OMP_AFFINITY_FORMAT = '%s'\n", gomp_affinity_format_var); - fprintf (stderr, " OMP_ALLOCATOR = '"); + fprintf (stderr, " [host] OMP_ALLOCATOR = '"); switch (gomp_def_allocator) { #define C(v) case v: fputs (#v, stderr); break; @@ -1387,7 +1782,7 @@ omp_display_env (int verbose) } fputs ("'\n", stderr); - fputs (" OMP_TARGET_OFFLOAD = '", stderr); + fputs (" [all] OMP_TARGET_OFFLOAD = '", stderr); switch (gomp_target_offload_var) { case GOMP_TARGET_OFFLOAD_DEFAULT: @@ -1404,13 +1799,13 @@ omp_display_env (int verbose) if (verbose) { - fputs (" GOMP_CPU_AFFINITY = ''\n", stderr); - fprintf (stderr, " GOMP_STACKSIZE = '%lu'\n", stacksize); + fputs (" [host] GOMP_CPU_AFFINITY = ''\n", stderr); + fprintf (stderr, " [host] GOMP_STACKSIZE = '%lu'\n", stacksize); #ifdef HAVE_INTTYPES_H - fprintf (stderr, " GOMP_SPINCOUNT = '%"PRIu64"'\n", + fprintf (stderr, " [host] GOMP_SPINCOUNT = '%"PRIu64"'\n", (uint64_t) gomp_spin_count_var); #else - fprintf (stderr, " GOMP_SPINCOUNT = '%lu'\n", + fprintf (stderr, " [host] GOMP_SPINCOUNT = '%lu'\n", (unsigned long) gomp_spin_count_var); #endif } @@ -1459,65 +1854,370 @@ handle_omp_display_env (void) ialias_call (omp_display_env) (verbose); } +/* Helper function for initialize_env. Extracts the device number from + an environment variable name. ENV is the complete environment variable. + DEV_NUM_PTR points to the start of the device number in the environment + variable string. DEV_NUM_LEN is the returned length of the device num + string. */ + +static bool +get_device_num (char *env, char *dev_num_ptr, int *dev_num, int *dev_num_len) +{ + char *end; + unsigned long val = strtoul (dev_num_ptr, &end, 10); + if (val > INT_MAX + || *end != '=' + || (dev_num_ptr[0] == '0' && end != dev_num_ptr + 1) + || (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; +} + +static void +get_icv_member_addr (struct gomp_initial_icvs *icvs, int icv_code, + void *icv_addr[3]) +{ + if (icv_code == 0 || icv_addr == NULL) + return; + + icv_addr[0] = icv_addr[1] = icv_addr[2] = NULL; + + switch (icv_code) + { + case GOMP_ICV_NTEAMS: + icv_addr[0] = &icvs->nteams_var; + icv_addr[1] = false; + break; + case GOMP_ICV_DYNAMIC: + icv_addr[0] = &(*icvs).dyn_var; + break; + case GOMP_ICV_TEAMS_THREAD_LIMIT: + icv_addr[0] = &icvs->teams_thread_limit_var; + icv_addr[1] = false; + break; + case GOMP_ICV_SCHEDULE: + icv_addr[0] = &icvs->run_sched_var; + icv_addr[1] = &icvs->run_sched_chunk_size; + break; + case GOMP_ICV_THREAD_LIMIT: + icv_addr[0] = &icvs->thread_limit_var; + icv_addr[1] = false; + icv_addr[2] = (void *) UINT_MAX; + break; + case GOMP_ICV_NTHREADS: + icv_addr[0] = &icvs->nthreads_var; + icv_addr[1] = &icvs->nthreads_var_list; + icv_addr[2] = &icvs->nthreads_var_list_len; + break; + case GOMP_ICV_MAX_ACTIVE_LEVELS: + icv_addr[0] = &icvs->max_active_levels_var; + icv_addr[1] = (void *) true; + icv_addr[2] = (void *) gomp_supported_active_levels; + break; + case GOMP_ICV_BIND: + icv_addr[0] = &icvs->bind_var; + icv_addr[1] = &icvs->bind_var_list; + icv_addr[2] = &icvs->bind_var_list_len; + break; + case GOMP_ICV_WAIT_POLICY: + icv_addr[0] = &icvs->wait_policy; + break; + case GOMP_ICV_STACKSIZE: + icv_addr[0] = &icvs->stacksize; + break; + case GOMP_ICV_CANCELLATION: + icv_addr[0] = &gomp_cancel_var; + break; + case GOMP_ICV_DISPLAY_AFFINITY: + icv_addr[0] = &gomp_display_affinity_var; + break; + case GOMP_ICV_TARGET_OFFLOAD: + icv_addr[0] = &gomp_target_offload_var; + break; + case GOMP_ICV_MAX_TASK_PRIORITY: + icv_addr[0] = &gomp_max_task_priority_var; + break; + case GOMP_ICV_ALLOCATOR: + icv_addr[0] = &gomp_def_allocator; + break; + case GOMP_ICV_DEFAULT_DEVICE: + icv_addr[0] = &icvs->default_device_var; + icv_addr[1] = (void *) true; + break; + } +} + +struct gomp_icv_list * +gomp_get_initial_icv_item (int dev_num) +{ + struct gomp_icv_list *l = gomp_initial_icv_list; + while (l != NULL && l->device_num != dev_num) + l = l->next; + + return l; +} + +static void +initialize_icvs (struct gomp_initial_icvs *icvs) +{ + icvs->nthreads_var_list = NULL; + icvs->bind_var_list = NULL; + icvs->nthreads_var = gomp_default_icv_values.nthreads_var; + icvs->nthreads_var_list_len = 0; + icvs->bind_var_list_len = 0; + icvs->stacksize = 0; + icvs->thread_limit_var = gomp_default_icv_values.thread_limit_var; + icvs->run_sched_var = gomp_default_icv_values.run_sched_var; + icvs->run_sched_chunk_size = gomp_default_icv_values.run_sched_chunk_size; + icvs->default_device_var = gomp_default_icv_values.default_device_var; + icvs->dyn_var = gomp_default_icv_values.dyn_var; + icvs->max_active_levels_var = gomp_default_icv_values.max_active_levels_var; + icvs->bind_var = gomp_default_icv_values.bind_var; + icvs->nteams_var = gomp_default_icv_values.nteams_var; + icvs->teams_thread_limit_var = gomp_default_icv_values.teams_thread_limit_var; + icvs->wait_policy = 0; +} + +/* Helper function for initialize_env to add a device specific ICV value + to gomp_initial_icv_list. */ + +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_cleared (sizeof (struct gomp_icv_list))); + l->device_num = dev_num; + initialize_icvs (&l->icvs); + 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); + + return &l->flags; +} + +/* Return true if STR string starts with PREFIX. */ + +static inline bool +startswith (const char *str, const char *prefix) +{ + return strncmp (str, prefix, strlen (prefix)) == 0; +} static void __attribute__((constructor)) initialize_env (void) { - unsigned long thread_limit_var; - unsigned long max_active_levels_var; + extern char **environ; + char **env; + int omp_var, dev_num = 0, dev_num_len = 0, i; + bool ignore = false; + char *env_val; + void *params[3]; + uint32_t *flag_var_addr = NULL; + unsigned pos; + struct gomp_icv_list *all, *none; /* Do a compile time check that mkomp_h.pl did good job. */ omp_check_defines (); - parse_schedule (); - parse_boolean ("OMP_DYNAMIC", &gomp_global_icv.dyn_var); - parse_boolean ("OMP_CANCELLATION", &gomp_cancel_var); - parse_boolean ("OMP_DISPLAY_AFFINITY", &gomp_display_affinity_var); - parse_int ("OMP_DEFAULT_DEVICE", &gomp_global_icv.default_device_var, true); - parse_target_offload ("OMP_TARGET_OFFLOAD", &gomp_target_offload_var); - parse_int ("OMP_MAX_TASK_PRIORITY", &gomp_max_task_priority_var, true); - gomp_def_allocator = parse_allocator (); - if (parse_unsigned_long ("OMP_THREAD_LIMIT", &thread_limit_var, false)) - { - gomp_global_icv.thread_limit_var - = thread_limit_var > INT_MAX ? UINT_MAX : thread_limit_var; - } - parse_int_secure ("GOMP_DEBUG", &gomp_debug_var, true); #ifndef HAVE_SYNC_BUILTINS gomp_mutex_init (&gomp_managed_threads_lock); #endif gomp_init_num_threads (); gomp_available_cpus = gomp_global_icv.nthreads_var; - if (!parse_unsigned_long_list ("OMP_NUM_THREADS", - &gomp_global_icv.nthreads_var, - &gomp_nthreads_var_list, - &gomp_nthreads_var_list_len)) - gomp_global_icv.nthreads_var = gomp_available_cpus; - parse_int ("OMP_NUM_TEAMS", &gomp_nteams_var, false); - parse_int ("OMP_TEAMS_THREAD_LIMIT", &gomp_teams_thread_limit_var, false); - bool ignore = false; - if (parse_bind_var ("OMP_PROC_BIND", - &gomp_global_icv.bind_var, - &gomp_bind_var_list, - &gomp_bind_var_list_len) + + /* 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 (GOMP_DEVICE_NUM_FOR_NO_SUFFIX, 0, NULL); + none = gomp_get_initial_icv_item (GOMP_DEVICE_NUM_FOR_NO_SUFFIX); + initialize_icvs (&none->icvs); + + for (env = environ; *env != 0; env++) + { + if (!startswith (*env, "OMP_")) + continue; + + /* Name of the environment variable without suffix "OMP_". */ + char *name = *env + sizeof ("OMP_") - 1; + for (omp_var = 0; omp_var < OMP_VAR_CNT; omp_var++) + { + if (startswith (name, envvars[omp_var].name)) + { + pos = envvars[omp_var].name_len; + if (name[pos] == '=') + { + pos++; + flag_var_addr + = add_initial_icv_to_list (GOMP_DEVICE_NUM_FOR_NO_SUFFIX, + envvars[omp_var].flag_vars[0], + params); + } + else if (startswith (&name[pos], "_DEV=") + && envvars[omp_var].flag & GOMP_ENV_SUFFIX_DEV) + { + pos += 5; + flag_var_addr + = add_initial_icv_to_list (GOMP_DEVICE_NUM_FOR_DEV, + envvars[omp_var].flag_vars[0], + params); + } + else if (startswith (&name[pos], "_ALL=") + && envvars[omp_var].flag & GOMP_ENV_SUFFIX_ALL) + { + pos += 5; + flag_var_addr + = add_initial_icv_to_list (GOMP_DEVICE_NUM_FOR_ALL, + envvars[omp_var].flag_vars[0], + params); + } + else if (startswith (&name[pos], "_DEV_") + && envvars[omp_var].flag & GOMP_ENV_SUFFIX_DEV_X) + { + pos += 5; + if (!get_device_num (*env, &name[pos], &dev_num, + &dev_num_len)) + 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 environment variable in %s", *env); + break; + } + env_val = &name[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; + } + } + } + + all = gomp_get_initial_icv_item (GOMP_DEVICE_NUM_FOR_ALL); + for (omp_var = 0; omp_var < OMP_HOST_VAR_CNT; omp_var++) + { + if (none != NULL + && gomp_get_icv_flag (none->flags, host_envvars[omp_var].flag_var)) + get_icv_member_addr (&none->icvs, + host_envvars[omp_var].flag_var, params); + else if (all != NULL + && gomp_get_icv_flag (all->flags, + host_envvars[omp_var].flag_var)) + get_icv_member_addr (&all->icvs, host_envvars[omp_var].flag_var, + params); + else + continue; + + switch (host_envvars[omp_var].type_code) + { + case PARSE_INT: + for (i = 0; i < 3; ++i) + if (host_envvars[omp_var].dest[i] != NULL && params[i] != NULL) + *(int *) (host_envvars[omp_var].dest[i]) = *(int *) params[i]; + break; + case PARSE_BOOL: + for (i = 0; i < 3; ++i) + if (host_envvars[omp_var].dest[i] != NULL && params[i] != NULL) + *(bool *) (host_envvars[omp_var].dest[i]) = *(bool *) params[i]; + break; + case PARSE_UINT: + for (i = 0; i < 3; ++i) + if (host_envvars[omp_var].dest[i] != NULL && params[i] != NULL) + *(unsigned int *) (host_envvars[omp_var].dest[i]) + = *(unsigned int *) params[i]; + break; + case PARSE_ULONG: + for (i = 0; i < 3; ++i) + if (host_envvars[omp_var].dest[i] != NULL && params[i] != NULL) + *(unsigned long *) (host_envvars[omp_var].dest[i]) + = *(unsigned long *) params[i]; + break; + case PARSE_UCHAR: + for (i = 0; i < 3; ++i) + if (host_envvars[omp_var].dest[i] != NULL && params[i] != NULL) + *(unsigned char *) (host_envvars[omp_var].dest[i]) + = *(unsigned char *) params[i]; + break; + case PARSE_SCHEDULE: + *(enum gomp_schedule_type *) (host_envvars[omp_var].dest[0]) + = *(enum gomp_schedule_type *) params[0]; + *(int *) (host_envvars[omp_var].dest[1]) = *(int *) params[1]; + break; + case PARSE_BIND: + *(char *) (host_envvars[omp_var].dest[0]) + = *(char *) params[0]; + *(char *) (host_envvars[omp_var].dest[1]) + = *(char *) params[1]; + *(unsigned long *) (host_envvars[omp_var].dest[2]) + = *(unsigned long *) params[2]; + break; + } + } + + if (((none != NULL && gomp_get_icv_flag (none->flags, GOMP_ICV_BIND)) + || (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_BIND))) && gomp_global_icv.bind_var == omp_proc_bind_false) ignore = true; - if (parse_unsigned_long ("OMP_MAX_ACTIVE_LEVELS", - &max_active_levels_var, true)) - gomp_global_icv.max_active_levels_var - = (max_active_levels_var > gomp_supported_active_levels) - ? gomp_supported_active_levels : max_active_levels_var; - else + + if (!((none != NULL + && gomp_get_icv_flag (none->flags, GOMP_ICV_MAX_ACTIVE_LEVELS)) + || (all != NULL + && gomp_get_icv_flag (all->flags, GOMP_ICV_MAX_ACTIVE_LEVELS)))) { bool nested = true; + const char *env = getenv ("OMP_NESTED"); /* OMP_NESTED is deprecated in OpenMP 5.0. */ - if (parse_boolean ("OMP_NESTED", &nested)) + if (parse_boolean ("OMP_NESTED", env, (void *[]) {&nested})) gomp_global_icv.max_active_levels_var = nested ? gomp_supported_active_levels : 1; else if (gomp_nthreads_var_list_len > 1 || gomp_bind_var_list_len > 1) gomp_global_icv.max_active_levels_var = gomp_supported_active_levels; } + + /* Process GOMP_* variables and dependencies between parsed ICVs. */ + parse_int_secure ("GOMP_DEBUG", &gomp_debug_var, true); + /* Make sure OMP_PLACES and GOMP_CPU_AFFINITY env vars are always parsed if present in the environment. If OMP_PROC_BIND was set explicitly to false, don't populate places list though. If places @@ -1547,7 +2247,11 @@ initialize_env (void) gomp_set_affinity_format (env, strlen (env)); } - wait_policy = parse_wait_policy (); + if (none != NULL && gomp_get_icv_flag (none->flags, GOMP_ICV_WAIT_POLICY)) + wait_policy = none->icvs.wait_policy; + else if (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_WAIT_POLICY)) + wait_policy = all->icvs.wait_policy; + if (!parse_spincount ("GOMP_SPINCOUNT", &gomp_spin_count_var)) { /* Using a rough estimation of 100000 spins per msec, @@ -1573,8 +2277,21 @@ initialize_env (void) /* Not strictly environment related, but ordering constructors is tricky. */ pthread_attr_init (&gomp_thread_attr); - if (parse_stacksize ("OMP_STACKSIZE", &stacksize) - || parse_stacksize ("GOMP_STACKSIZE", &stacksize) + if (!(none != NULL && gomp_get_icv_flag (none->flags, GOMP_ICV_STACKSIZE))) + { + const char *env = getenv ("GOMP_STACKSIZE"); + if (env != NULL + && parse_stacksize ("GOMP_STACKSIZE", env, + (void *[3]) {&none->icvs.stacksize})) + gomp_set_icv_flag (&none->flags, GOMP_ICV_STACKSIZE); + } + if (none != NULL && gomp_get_icv_flag (none->flags, GOMP_ICV_STACKSIZE)) + stacksize = none->icvs.stacksize; + else if (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_STACKSIZE)) + stacksize = all->icvs.stacksize; + + if ((none != NULL && gomp_get_icv_flag (none->flags, GOMP_ICV_STACKSIZE)) + || (all != NULL && gomp_get_icv_flag (all->flags, GOMP_ICV_STACKSIZE)) || GOMP_DEFAULT_STACKSIZE) { int err; @@ -1601,7 +2318,8 @@ initialize_env (void) /* OpenACC. */ - if (!parse_int ("ACC_DEVICE_NUM", &goacc_device_num, true)) + if (!parse_int ("ACC_DEVICE_NUM", getenv ("ACC_DEVICE_NUM"), + (void *[]) {&goacc_device_num, (void *) true})) goacc_device_num = 0; parse_acc_device_type (); |