LLVM OpenMP
kmp_device_env.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// OpenMP 6.0 device-scope env-var registry. See kmp_device_env.h for the
10// public-internal contract;
11//
12//===----------------------------------------------------------------------===//
13
14#include "kmp.h"
15#include "kmp_device_env.h"
16#include "kmp_i18n.h"
17#include "kmp_str.h"
18
19#include <stdlib.h>
20#include <string.h>
21
22// Eligible-var table. Only env vars associated with non-global-scope ICVs
23// (and not `OMP_DEFAULT_DEVICE`) are eligible for `_ALL`/`_DEV[_d]` forms,
24// per OpenMP 6.0.
25static char const *const __kmp_device_env_eligible_names[] = {
26 "OMP_NUM_THREADS",
27 NULL,
28};
29
30// Denylist of well-known OpenMP env vars that initialize *global-scope* ICVs
31// or `OMP_DEFAULT_DEVICE`.
32static char const *const __kmp_device_env_denied_bases[] = {
33 "OMP_DEFAULT_DEVICE",
34 "OMP_MAX_TASK_PRIORITY",
35 "OMP_TARGET_OFFLOAD",
36 "OMP_DISPLAY_ENV",
37 "OMP_DISPLAY_AFFINITY",
38 "OMP_AFFINITY_FORMAT",
39 "OMP_CANCELLATION",
40 "OMP_TOOL",
41 "OMP_TOOL_LIBRARIES",
42 "OMP_TOOL_VERBOSE_INIT",
43 "OMP_DEBUG",
44 NULL,
45};
46
52
54 char *all_value; // <ENV>_ALL
55 char *dev_default_value; // <ENV>_DEV
57};
58
60
61static int __kmp_device_env_count(void) {
62 return sizeof(__kmp_device_env_eligible_names) /
64 1;
65}
66
67// Invariant: the eligible-name and denied-base tables must be disjoint --
68// the classifier short-circuits on the first eligible match and never
69// consults the denylist (see `__kmp_classify_device_env_name`). Verified
70// once-per-process in debug builds to protect future maintainers.
72#ifdef KMP_DEBUG
73 for (int i = 0; __kmp_device_env_eligible_names[i] != NULL; ++i) {
74 for (int j = 0; __kmp_device_env_denied_bases[j] != NULL; ++j) {
77 }
78 }
79#endif
80}
81
82static void __kmp_device_env_lazy_init(void) {
83 if (__kmp_device_env_states != NULL)
84 return;
89 for (int i = 0; i < count; ++i) {
90 __kmp_device_env_states[i].all_value = NULL;
91 __kmp_device_env_states[i].dev_default_value = NULL;
92 __kmp_device_env_states[i].per_device = NULL;
93 }
94}
95
96static int __kmp_device_env_index(char const *base_name) {
97 if (base_name == NULL)
98 return -1;
99 for (int i = 0; __kmp_device_env_eligible_names[i] != NULL; ++i)
100 if (strcmp(__kmp_device_env_eligible_names[i], base_name) == 0)
101 return i;
102 return -1;
103}
104
105extern "C" char const *__kmp_device_env_eligible_name(int index) {
106 if (index < 0)
107 return NULL;
109 if (index >= count)
110 return NULL;
112}
113
114// Match `full_name` against `base_name + suffix`. Returns the pointer just
115// after the matched suffix on success, or NULL on failure.
116static char const *__kmp_strip_prefix_and_suffix(char const *full_name,
117 char const *base_name,
118 char const *suffix) {
119 size_t base_len = strlen(base_name);
120 if (strncmp(full_name, base_name, base_len) != 0)
121 return NULL;
122 char const *rest = full_name + base_len;
123 size_t sfx_len = strlen(suffix);
124 if (strncmp(rest, suffix, sfx_len) != 0)
125 return NULL;
126 return rest + sfx_len;
127}
128
129// True if `s` is non-empty and consists entirely of ASCII decimal digits.
130static int __kmp_is_nonneg_int(char const *s) {
131 if (s == NULL || *s == '\0')
132 return 0;
133 for (char const *p = s; *p; ++p) {
134 if (*p < '0' || *p > '9')
135 return 0;
136 }
137 return 1;
138}
139
140// Returns the parsed value on success, -1 on overflow/empty/non-digit input.
141// Leading zeros are allowed (e.g., `000000000001` is device 1). INT_MAX and
142// larger values are rejected so -1 remains the only error sentinel.
143static int __kmp_parse_dev_id(char const *s) {
145 return -1;
146 long long v = 0;
147 for (char const *p = s; *p; ++p) {
148 v = v * 10 + (*p - '0');
149 if (v >= (long long)INT_MAX)
150 return -1;
151 }
152 return (int)v;
153}
154
161
162// Try to classify `full_name` as `<base>_<suffix>` where suffix is `_ALL`,
163// `_DEV`, or `_DEV_<token>`. On match, returns the kind and (for `_DEV_<n>`)
164// fills `*out_dev_id` (or sets it to -1 to flag a malformed token, e.g. a
165// non-integer or overflowing token). Returns kmp_dev_env_none if `full_name`
166// does not have any of those exact suffixes for `base`.
168__kmp_match_suffix(char const *full_name, char const *base, int *out_dev_id) {
169 *out_dev_id = -1;
170 if (char const *tail = __kmp_strip_prefix_and_suffix(full_name, base, "_ALL"))
171 if (*tail == '\0')
172 return kmp_dev_env_all;
173 if (char const *tail =
174 __kmp_strip_prefix_and_suffix(full_name, base, "_DEV_")) {
175 int dev_id = __kmp_parse_dev_id(tail);
176 *out_dev_id = dev_id; // -1 signals malformed/overflowing token
177 return kmp_dev_env_dev_id;
178 }
179 if (char const *tail = __kmp_strip_prefix_and_suffix(full_name, base, "_DEV"))
180 if (*tail == '\0')
182 return kmp_dev_env_none;
183}
184
185// Decompose `full_name` into base index, kind and (optionally) device id.
186// Sets `*out_dev_id` only when the kind is `kmp_dev_env_dev_id`.
187//
188// `*out_index` is set to the eligible-table index when the base matches an
189// eligible name, or -1 if `full_name` matches a *denied* (global-scope)
190// base + suffix (the caller emits a warning in that case).
191//
192// Returns the kind. `kmp_dev_env_none` means the name is not a device-scope
193// variant of either an eligible base or a known denied base. The caller
194// should treat such names normally (no warning) so unrelated env vars like
195// `OMP_DEV_LIST` are not misinterpreted.
197 int *out_index,
198 int *out_dev_id) {
199 *out_index = -1;
200 *out_dev_id = -1;
201
202 if (full_name == NULL || *full_name == '\0')
203 return kmp_dev_env_none;
204
205 // First, try to recognize a known eligible base + suffix pairing.
206 for (int i = 0; __kmp_device_env_eligible_names[i] != NULL; ++i) {
207 char const *base = __kmp_device_env_eligible_names[i];
208 int dev_id = -1;
209 kmp_dev_env_kind_t k = __kmp_match_suffix(full_name, base, &dev_id);
210 if (k != kmp_dev_env_none) {
211 *out_index = i;
212 *out_dev_id = dev_id;
213 return k;
214 }
215 }
216
217 // Second, check the denylist of well-known global-scope OMP env vars. Only
218 // these well-defined bases trigger the global-scope-rejection warning;
219 // unrelated user-defined env vars (e.g. `OMP_DEV_LIST`, `KMP_X`) are
220 // ignored silently.
221 for (int i = 0; __kmp_device_env_denied_bases[i] != NULL; ++i) {
222 char const *base = __kmp_device_env_denied_bases[i];
223 int dev_id = -1;
224 kmp_dev_env_kind_t k = __kmp_match_suffix(full_name, base, &dev_id);
225 if (k != kmp_dev_env_none)
226 return k; // *out_index stays -1 -- denylist hit
227 }
228
229 return kmp_dev_env_none;
230}
231
232static void __kmp_device_env_set_string(char **slot, char const *value) {
233 if (*slot != NULL) {
234 __kmp_str_free(slot);
235 }
236 *slot = __kmp_str_format("%s", value);
237}
238
240 int device_id, char const *value) {
241 for (kmp_device_env_dev_node_t *n = st->per_device; n != NULL; n = n->next) {
242 if (n->device_id == device_id) {
243 __kmp_str_free(&n->value);
244 n->value = __kmp_str_format("%s", value);
245 return;
246 }
247 }
251 node->device_id = device_id;
252 node->value = __kmp_str_format("%s", value);
253 node->next = st->per_device;
254 st->per_device = node;
255}
256
257extern "C" int __kmp_device_env_record(char const *full_name,
258 char const *value) {
259 if (full_name == NULL || value == NULL)
260 return 0;
261
262 int idx = -1;
263 int dev_id = -1;
264 kmp_dev_env_kind_t kind =
265 __kmp_classify_device_env_name(full_name, &idx, &dev_id);
266 if (kind == kmp_dev_env_none)
267 return 0; // not a device-scope variant -- caller handles normally
268
269 // Suffix recognized but base is on the denylist (global-scope ICV). Per the
270 // OpenMP 6.0 restriction, reject with a warning.
271 if (idx < 0) {
272 KMP_WARNING(DeviceEnvVarOnGlobalScope, full_name);
273 return 1;
274 }
275
276 // For the eligible-base path, malformed `<ENV>_DEV_<token>` (non-integer or
277 // overflowing) is rejected before we touch the registry.
278 if (kind == kmp_dev_env_dev_id && dev_id < 0) {
279 KMP_WARNING(MalformedDeviceEnvVar, full_name);
280 return 1;
281 }
282
285
286 switch (kind) {
287 case kmp_dev_env_all:
289 return 1;
292 return 1;
295 return 1;
296 case kmp_dev_env_none:
297 break;
298 }
299 return 0;
300}
301
302// Look up the registry state for `base_name`, or NULL if it is not an
303// eligible base or nothing was recorded yet.
304static kmp_device_env_state_t *__kmp_device_env_lookup(char const *base_name) {
305 int idx = __kmp_device_env_index(base_name);
306 if (idx < 0 || __kmp_device_env_states == NULL)
307 return NULL;
308 return &__kmp_device_env_states[idx];
309}
310
311extern "C" char const *__kmpc_get_device_env(char const *base_name,
312 int device_id) {
313 // Host ICVs are observed through the existing OpenMP APIs. This entry
314 // point is for non-host devices only and takes a 0-based device id.
315 KMP_DEBUG_ASSERT(device_id >= 0);
316 if (device_id < 0)
317 return NULL;
319 if (st == NULL)
320 return NULL;
321
322 // Non-host device d: <ENV>_DEV_<d> > <ENV>_DEV > <ENV>_ALL > default.
323 for (kmp_device_env_dev_node_t *n = st->per_device; n != NULL; n = n->next) {
324 if (n->device_id == device_id)
325 return n->value;
326 }
327 if (st->dev_default_value != NULL)
328 return st->dev_default_value;
329 if (st->all_value != NULL)
330 return st->all_value;
331 return NULL;
332}
333
334extern "C" void __kmp_device_env_reset(void) {
335 if (__kmp_device_env_states == NULL)
336 return;
338 for (int i = 0; i < count; ++i) {
343 while (n) {
347 n = next;
348 }
349 st->per_device = NULL;
350 }
353}
void const char const char int ITT_FORMAT __itt_group_sync s
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t count
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long value
void const char const char int ITT_FORMAT __itt_group_sync p
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle __itt_metadata_type size_t void ITT_FORMAT p const __itt_domain __itt_id __itt_string_handle const wchar_t size_t ITT_FORMAT lu const __itt_domain __itt_id __itt_relation __itt_id tail
#define KMP_INTERNAL_MALLOC(sz)
Definition kmp.h:103
#define KMP_INTERNAL_FREE(p)
Definition kmp.h:104
#define KMP_DEBUG_ASSERT(cond)
Definition kmp_debug.h:61
static char const * __kmp_strip_prefix_and_suffix(char const *full_name, char const *base_name, char const *suffix)
kmp_dev_env_kind_t
@ kmp_dev_env_none
@ kmp_dev_env_dev_default
@ kmp_dev_env_all
@ kmp_dev_env_dev_id
static void __kmp_device_env_set_per_device(kmp_device_env_state_t *st, int device_id, char const *value)
char const * __kmp_device_env_eligible_name(int index)
static char const *const __kmp_device_env_denied_bases[]
static int __kmp_is_nonneg_int(char const *s)
static void __kmp_device_env_assert_tables_disjoint(void)
static int __kmp_device_env_index(char const *base_name)
static kmp_dev_env_kind_t __kmp_classify_device_env_name(char const *full_name, int *out_index, int *out_dev_id)
static int __kmp_parse_dev_id(char const *s)
static char const *const __kmp_device_env_eligible_names[]
static kmp_device_env_state_t * __kmp_device_env_states
void __kmp_device_env_reset(void)
static void __kmp_device_env_lazy_init(void)
int __kmp_device_env_record(char const *full_name, char const *value)
char const * __kmpc_get_device_env(char const *base_name, int device_id)
static int __kmp_device_env_count(void)
static kmp_device_env_state_t * __kmp_device_env_lookup(char const *base_name)
static kmp_dev_env_kind_t __kmp_match_suffix(char const *full_name, char const *base, int *out_dev_id)
static void __kmp_device_env_set_string(char **slot, char const *value)
#define KMP_WARNING(...)
Definition kmp_i18n.h:144
char * __kmp_str_format(char const *format,...)
Definition kmp_str.cpp:448
void __kmp_str_free(char **str)
Definition kmp_str.cpp:494
#define i
Definition kmp_stub.cpp:88
struct kmp_device_env_dev_node_t * next
kmp_device_env_dev_node_t * per_device