[Openmp-commits] [openmp] [OpenMP][Runtime] OpenMP 6.0 Device-Scope Environment Variables (PR #197191)
Amit Tiwari via Openmp-commits
openmp-commits at lists.llvm.org
Mon Jun 22 02:52:19 PDT 2026
https://github.com/loopacino updated https://github.com/llvm/llvm-project/pull/197191
>From 43cc1bcf95eaa06c456798a804568aff44fbd923 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Mon, 4 May 2026 07:16:10 -0400
Subject: [PATCH 1/8] device-scope_env_var
---
openmp/runtime/src/CMakeLists.txt | 1 +
openmp/runtime/src/kmp_device_env.cpp | 357 ++++++++++++++++++++++++++
openmp/runtime/src/kmp_device_env.h | 69 +++++
openmp/runtime/src/kmp_settings.cpp | 29 ++-
4 files changed, 455 insertions(+), 1 deletion(-)
create mode 100644 openmp/runtime/src/kmp_device_env.cpp
create mode 100644 openmp/runtime/src/kmp_device_env.h
diff --git a/openmp/runtime/src/CMakeLists.txt b/openmp/runtime/src/CMakeLists.txt
index 463b8fe2f14aa..1ac72487920dc 100644
--- a/openmp/runtime/src/CMakeLists.txt
+++ b/openmp/runtime/src/CMakeLists.txt
@@ -85,6 +85,7 @@ else()
kmp_atomic.cpp
kmp_csupport.cpp
kmp_debug.cpp
+ kmp_device_env.cpp
kmp_itt.cpp
kmp_invoke_microtask.cpp
kmp_environment.cpp
diff --git a/openmp/runtime/src/kmp_device_env.cpp b/openmp/runtime/src/kmp_device_env.cpp
new file mode 100644
index 0000000000000..b4e3b336a1894
--- /dev/null
+++ b/openmp/runtime/src/kmp_device_env.cpp
@@ -0,0 +1,357 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// OpenMP 6.0 device-scope env-var registry. See kmp_device_env.h for the
+// public-internal contract; see openmp/docs/OpenMP_6_0_DeviceScope_EnvVars_
+// Walkthrough.md for the design notes and examples.
+//
+//===----------------------------------------------------------------------===//
+
+#include "kmp.h"
+#include "kmp_device_env.h"
+#include "kmp_i18n.h"
+#include "kmp_str.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Eligible-var table. Only env vars associated with non-global-scope ICVs
+// (and not `OMP_DEFAULT_DEVICE`) are eligible for `_ALL`/`_DEV[_d]` forms,
+// per OpenMP 6.0.
+static char const *const __kmp_device_env_eligible_names[] = {
+ "OMP_NUM_THREADS",
+ NULL,
+};
+
+// Denylist of well-known OpenMP env vars that initialize *global-scope* ICVs
+// or `OMP_DEFAULT_DEVICE`.
+static char const *const __kmp_device_env_denied_bases[] = {
+ "OMP_DEFAULT_DEVICE",
+ "OMP_MAX_TASK_PRIORITY",
+ "OMP_TARGET_OFFLOAD",
+ "OMP_DISPLAY_ENV",
+ "OMP_DISPLAY_AFFINITY",
+ "OMP_AFFINITY_FORMAT",
+ "OMP_CANCELLATION",
+ "OMP_TOOL",
+ "OMP_TOOL_LIBRARIES",
+ "OMP_TOOL_VERBOSE_INIT",
+ "OMP_DEBUG",
+ NULL,
+};
+
+struct kmp_device_env_dev_node_t {
+ int device_id;
+ char *value;
+ struct kmp_device_env_dev_node_t *next;
+};
+
+struct kmp_device_env_state_t {
+ char *host_value; // <ENV>
+ char *all_value; // <ENV>_ALL
+ char *dev_default_value; // <ENV>_DEV
+ kmp_device_env_dev_node_t *per_device; // <ENV>_DEV_<d>
+};
+
+static kmp_device_env_state_t *__kmp_device_env_states = NULL;
+
+static int __kmp_device_env_count(void) {
+ int count = 0;
+ while (__kmp_device_env_eligible_names[count] != NULL)
+ ++count;
+ return count;
+}
+
+static void __kmp_device_env_lazy_init(void) {
+ if (__kmp_device_env_states != NULL)
+ return;
+ int count = __kmp_device_env_count();
+ __kmp_device_env_states = (kmp_device_env_state_t *)KMP_INTERNAL_MALLOC(
+ sizeof(kmp_device_env_state_t) * count);
+ for (int i = 0; i < count; ++i) {
+ __kmp_device_env_states[i].host_value = NULL;
+ __kmp_device_env_states[i].all_value = NULL;
+ __kmp_device_env_states[i].dev_default_value = NULL;
+ __kmp_device_env_states[i].per_device = NULL;
+ }
+}
+
+static int __kmp_device_env_index(char const *base_name) {
+ if (base_name == NULL)
+ return -1;
+ for (int i = 0; __kmp_device_env_eligible_names[i] != NULL; ++i)
+ if (strcmp(__kmp_device_env_eligible_names[i], base_name) == 0)
+ return i;
+ return -1;
+}
+
+extern "C" char const *__kmp_device_env_eligible_name(int index) {
+ if (index < 0)
+ return NULL;
+ int count = __kmp_device_env_count();
+ if (index >= count)
+ return NULL;
+ return __kmp_device_env_eligible_names[index];
+}
+
+// Match `full_name` against `base_name + suffix`. Returns the pointer just
+// after the matched suffix on success, or NULL on failure.
+static char const *__kmp_strip_prefix_and_suffix(char const *full_name,
+ char const *base_name,
+ char const *suffix) {
+ size_t base_len = strlen(base_name);
+ if (strncmp(full_name, base_name, base_len) != 0)
+ return NULL;
+ char const *rest = full_name + base_len;
+ size_t sfx_len = strlen(suffix);
+ if (strncmp(rest, suffix, sfx_len) != 0)
+ return NULL;
+ return rest + sfx_len;
+}
+
+// True if `s` is non-empty and consists entirely of ASCII decimal digits.
+static int __kmp_is_nonneg_int(char const *s) {
+ if (s == NULL || *s == '\0')
+ return 0;
+ for (char const *p = s; *p; ++p) {
+ if (*p < '0' || *p > '9')
+ return 0;
+ }
+ return 1;
+}
+
+// Returns the parsed value on success, -1 on overflow/empty/non-digit input.
+// We cap valid device ids at INT_MAX-1 to leave INT_MAX as a sentinel.
+static int __kmp_parse_dev_id(char const *s) {
+ if (!__kmp_is_nonneg_int(s))
+ return -1;
+ // Reject obviously-overflowing inputs early.
+ size_t len = strlen(s);
+ if (len > 10)
+ return -1;
+ long long v = 0;
+ for (char const *p = s; *p; ++p) {
+ v = v * 10 + (*p - '0');
+ if (v >= 2147483647LL)
+ return -1;
+ }
+ return (int)v;
+}
+
+enum kmp_dev_env_kind_t {
+ kmp_dev_env_none = 0,
+ kmp_dev_env_all, // <ENV>_ALL
+ kmp_dev_env_dev_default, // <ENV>_DEV
+ kmp_dev_env_dev_id, // <ENV>_DEV_<d>
+};
+
+// Try to classify `full_name` as `<base>_<suffix>` where suffix is `_ALL`,
+// `_DEV`, or `_DEV_<token>`. On match, returns the kind and (for `_DEV_<n>`)
+// fills `*out_dev_id` (or sets it to -1 to flag a malformed token, e.g. a
+// non-integer or overflowing token). Returns kmp_dev_env_none if `full_name`
+// does not have any of those exact suffixes for `base`.
+static kmp_dev_env_kind_t
+__kmp_match_suffix(char const *full_name, char const *base, int *out_dev_id) {
+ *out_dev_id = -1;
+ if (char const *tail = __kmp_strip_prefix_and_suffix(full_name, base, "_ALL"))
+ if (*tail == '\0')
+ return kmp_dev_env_all;
+ if (char const *tail =
+ __kmp_strip_prefix_and_suffix(full_name, base, "_DEV_")) {
+ int dev_id = __kmp_parse_dev_id(tail);
+ *out_dev_id = dev_id; // -1 signals malformed/overflowing token
+ return kmp_dev_env_dev_id;
+ }
+ if (char const *tail = __kmp_strip_prefix_and_suffix(full_name, base, "_DEV"))
+ if (*tail == '\0')
+ return kmp_dev_env_dev_default;
+ return kmp_dev_env_none;
+}
+
+// Decompose `full_name` into base index, kind and (optionally) device id.
+// Sets `*out_dev_id` only when the kind is `kmp_dev_env_dev_id`.
+//
+// `*out_index` is set to the eligible-table index when the base matches an
+// eligible name, or -1 if `full_name` matches a *denied* (global-scope)
+// base + suffix (the caller emits a warning in that case).
+//
+// Returns the kind. `kmp_dev_env_none` means the name is not a device-scope
+// variant of either an eligible base or a known denied base. The caller
+// should treat such names normally (no warning) so unrelated env vars like
+// `OMP_DEV_LIST` are not misinterpreted.
+static kmp_dev_env_kind_t __kmp_classify_device_env_name(char const *full_name,
+ int *out_index,
+ int *out_dev_id) {
+ *out_index = -1;
+ *out_dev_id = -1;
+
+ if (full_name == NULL || *full_name == '\0')
+ return kmp_dev_env_none;
+
+ // First, try to recognize a known eligible base + suffix pairing.
+ for (int i = 0; __kmp_device_env_eligible_names[i] != NULL; ++i) {
+ char const *base = __kmp_device_env_eligible_names[i];
+ int dev_id = -1;
+ kmp_dev_env_kind_t k = __kmp_match_suffix(full_name, base, &dev_id);
+ if (k != kmp_dev_env_none) {
+ *out_index = i;
+ *out_dev_id = dev_id;
+ return k;
+ }
+ }
+
+ // Second, check the denylist of well-known global-scope OMP env vars. Only
+ // these well-defined bases trigger the global-scope-rejection warning;
+ // unrelated user-defined env vars (e.g. `OMP_DEV_LIST`, `KMP_X`) are
+ // ignored silently.
+ for (int i = 0; __kmp_device_env_denied_bases[i] != NULL; ++i) {
+ char const *base = __kmp_device_env_denied_bases[i];
+ int dev_id = -1;
+ kmp_dev_env_kind_t k = __kmp_match_suffix(full_name, base, &dev_id);
+ if (k != kmp_dev_env_none)
+ return k; // *out_index stays -1 -- denylist hit
+ }
+
+ return kmp_dev_env_none;
+}
+
+static void __kmp_device_env_set_string(char **slot, char const *value) {
+ if (*slot != NULL) {
+ __kmp_str_free(slot);
+ }
+ *slot = __kmp_str_format("%s", value);
+}
+
+static void __kmp_device_env_set_per_device(kmp_device_env_state_t *st,
+ int device_id, char const *value) {
+ for (kmp_device_env_dev_node_t *n = st->per_device; n != NULL; n = n->next) {
+ if (n->device_id == device_id) {
+ __kmp_str_free(&n->value);
+ n->value = __kmp_str_format("%s", value);
+ return;
+ }
+ }
+ kmp_device_env_dev_node_t *node =
+ (kmp_device_env_dev_node_t *)KMP_INTERNAL_MALLOC(
+ sizeof(kmp_device_env_dev_node_t));
+ node->device_id = device_id;
+ node->value = __kmp_str_format("%s", value);
+ node->next = st->per_device;
+ st->per_device = node;
+}
+
+extern "C" int __kmp_device_env_record(char const *full_name,
+ char const *value) {
+ if (full_name == NULL || value == NULL)
+ return 0;
+
+ int idx = -1;
+ int dev_id = -1;
+ kmp_dev_env_kind_t kind =
+ __kmp_classify_device_env_name(full_name, &idx, &dev_id);
+ if (kind == kmp_dev_env_none)
+ return 0; // not a device-scope variant -- caller handles normally
+
+ // Suffix recognized but base is on the denylist (global-scope ICV). Per the
+ // OpenMP 6.0 restriction, reject with a warning.
+ if (idx < 0) {
+ KMP_WARNING(DeviceEnvVarOnGlobalScope, full_name);
+ return 1;
+ }
+
+ // For the eligible-base path, malformed `<ENV>_DEV_<token>` (non-integer or
+ // overflowing) is rejected before we touch the registry.
+ if (kind == kmp_dev_env_dev_id && dev_id < 0) {
+ KMP_WARNING(MalformedDeviceEnvVar, full_name);
+ return 1;
+ }
+
+ __kmp_device_env_lazy_init();
+ kmp_device_env_state_t *st = &__kmp_device_env_states[idx];
+
+ switch (kind) {
+ case kmp_dev_env_all:
+ __kmp_device_env_set_string(&st->all_value, value);
+ return 1;
+ case kmp_dev_env_dev_default:
+ __kmp_device_env_set_string(&st->dev_default_value, value);
+ return 1;
+ case kmp_dev_env_dev_id:
+ __kmp_device_env_set_per_device(st, dev_id, value);
+ return 1;
+ case kmp_dev_env_none:
+ break;
+ }
+ return 0;
+}
+
+extern "C" char const *__kmp_resolve_device_env(char const *base_name,
+ int device_id) {
+ int idx = __kmp_device_env_index(base_name);
+ if (idx < 0 || __kmp_device_env_states == NULL)
+ return NULL;
+ kmp_device_env_state_t *st = &__kmp_device_env_states[idx];
+
+ if (device_id < 0) {
+ // Host: <ENV> > <ENV>_ALL > default.
+ if (st->host_value != NULL)
+ return st->host_value;
+ if (st->all_value != NULL)
+ return st->all_value;
+ return NULL;
+ }
+
+ // Non-host device d: <ENV>_DEV_<d> > <ENV>_DEV > <ENV>_ALL > default.
+ for (kmp_device_env_dev_node_t *n = st->per_device; n != NULL; n = n->next) {
+ if (n->device_id == device_id)
+ return n->value;
+ }
+ if (st->dev_default_value != NULL)
+ return st->dev_default_value;
+ if (st->all_value != NULL)
+ return st->all_value;
+ return NULL;
+}
+
+extern "C" void __kmp_device_env_observe_host(char const *full_name,
+ char const *value) {
+ int idx = __kmp_device_env_index(full_name);
+ if (idx < 0 || value == NULL)
+ return;
+ __kmp_device_env_lazy_init();
+ __kmp_device_env_set_string(&__kmp_device_env_states[idx].host_value, value);
+}
+
+extern "C" void __kmp_device_env_reset(void) {
+ if (__kmp_device_env_states == NULL)
+ return;
+ int count = __kmp_device_env_count();
+ for (int i = 0; i < count; ++i) {
+ kmp_device_env_state_t *st = &__kmp_device_env_states[i];
+ __kmp_str_free(&st->host_value);
+ __kmp_str_free(&st->all_value);
+ __kmp_str_free(&st->dev_default_value);
+ kmp_device_env_dev_node_t *n = st->per_device;
+ while (n) {
+ kmp_device_env_dev_node_t *next = n->next;
+ __kmp_str_free(&n->value);
+ KMP_INTERNAL_FREE(n);
+ n = next;
+ }
+ st->per_device = NULL;
+ }
+ KMP_INTERNAL_FREE(__kmp_device_env_states);
+ __kmp_device_env_states = NULL;
+}
+
+// Public test/query helper. Returns the resolved value for `name` on
+// `device_id`.
+extern "C" char const *__kmpc_get_resolved_device_env(char const *name,
+ int device_id) {
+ return __kmp_resolve_device_env(name, device_id);
+}
diff --git a/openmp/runtime/src/kmp_device_env.h b/openmp/runtime/src/kmp_device_env.h
new file mode 100644
index 0000000000000..e40b61743daab
--- /dev/null
+++ b/openmp/runtime/src/kmp_device_env.h
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Implements parsing, storage and precedence resolution for the OpenMP 6.0
+// device-scope environment variable extensions described in OpenMP 6.0
+// Section 3.2 (ICV initialization) and Chapter 4 (Environment Variables):
+//
+// <ENV> -- sets the host device ICV.
+// <ENV>_ALL -- sets the host and non-host device ICVs that are not
+// overridden by a more specific form.
+// <ENV>_DEV -- sets all non-host device ICVs that are not overridden
+// by an `<ENV>_DEV_<d>` form.
+// <ENV>_DEV_<d> -- sets the ICV for non-host device with id `d` (a
+// non-negative integer).
+//
+// Precedence:
+// Host: <ENV> > <ENV>_ALL > default
+// Device d: <ENV>_DEV_<d> > <ENV>_DEV > <ENV>_ALL > default
+//
+// Restrictions enforced:
+// * Device-specific environment variables are only accepted for env vars
+// listed in `__kmp_device_env_table` (i.e. those that initialize
+// device-scope ICVs and are not global-scope or `OMP_DEFAULT_DEVICE`).
+// Suffix forms applied to other env vars are ignored with a warning.
+// * `<ENV>_DEV_<token>` where <token> is not a non-negative integer is
+// rejected with a warning. The spec requires that device-specific
+// environment variables must not specify the host device; we accept only
+// non-negative integer non-host device ids.
+// * Conflicting settings of `<ENV>_DEV_<d>` (same d) follow last-parsed-wins
+// semantics, mirroring existing libomp env handling.
+
+#ifndef KMP_DEVICE_ENV_H
+#define KMP_DEVICE_ENV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Consume `full_name=value` if it is a device-scope variant. Returns:
+// 1 -- consumed (stored, OR rejected with a warning); caller must skip it.
+// 0 -- not a device-scope variant; caller continues normal processing.
+int __kmp_device_env_record(char const *full_name, char const *value);
+
+// Resolve the effective string for `base_name` on `device_id` per the
+// precedence rules above. Pass `device_id == -1` to request the host;
+// any other negative value returns NULL (defensive). Returns NULL when no
+// source applies (caller falls back to its own default).
+char const *__kmp_resolve_device_env(char const *base_name, int device_id);
+
+// Record an unsuffixed `<ENV>=value` pair so the host query is consistent
+// with the host ICV. No-op for non-eligible names.
+void __kmp_device_env_observe_host(char const *full_name, char const *value);
+
+// Free all storage owned by the registry.
+void __kmp_device_env_reset(void);
+
+// Iterate the eligible base-name table; returns NULL once exhausted.
+char const *__kmp_device_env_eligible_name(int index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // KMP_DEVICE_ENV_H
diff --git a/openmp/runtime/src/kmp_settings.cpp b/openmp/runtime/src/kmp_settings.cpp
index 66ef6f8097dce..63cb6b885f9e8 100644
--- a/openmp/runtime/src/kmp_settings.cpp
+++ b/openmp/runtime/src/kmp_settings.cpp
@@ -16,6 +16,7 @@
#if KMP_USE_HIER_SCHED
#include "kmp_dispatch_hier.h"
#endif
+#include "kmp_device_env.h"
#include "kmp_environment.h"
#include "kmp_i18n.h"
#include "kmp_io.h"
@@ -6089,6 +6090,8 @@ static void __kmp_aux_env_initialize(kmp_env_blk_t *block) {
/* OMP_NUM_THREADS */
value = __kmp_env_blk_var(block, "OMP_NUM_THREADS");
+ if (value == NULL)
+ value = __kmp_env_blk_var(block, "OMP_NUM_THREADS_ALL");
if (value) {
ompc_set_num_threads(__kmp_dflt_team_nth);
}
@@ -6133,7 +6136,8 @@ void __kmp_env_initialize(char const *string) {
}
__kmp_env_blk_init(&block, string);
- // update the set flag on all entries that have an env var
+ // Device-scope env-var pre-pass: route `<ENV>_ALL`,
+ // `<ENV>_DEV`, `<ENV>_DEV_<d>` into the device-scope registry.
for (i = 0; i < block.count; ++i) {
if ((block.vars[i].name == NULL) || (*block.vars[i].name == '\0')) {
continue;
@@ -6141,6 +6145,10 @@ void __kmp_env_initialize(char const *string) {
if (block.vars[i].value == NULL) {
continue;
}
+ if (__kmp_device_env_record(block.vars[i].name, block.vars[i].value))
+ continue;
+ __kmp_device_env_observe_host(block.vars[i].name, block.vars[i].value);
+
kmp_setting_t *setting = __kmp_stg_find(block.vars[i].name);
if (setting != NULL) {
setting->set = 1;
@@ -6250,6 +6258,25 @@ void __kmp_env_initialize(char const *string) {
__kmp_stg_parse(block.vars[i].name, block.vars[i].value);
}
+ // Device-scope env-var post-pass: when the current block has `<ENV>_ALL` but
+ // no unsuffixed `<ENV>`, replay `_ALL` so the host ICV picks it up. We query
+ // the block (not the registry) so an unrelated kmp_set_defaults does not
+ // accidentally replay a stale `_ALL`.
+ for (int e = 0;; ++e) {
+ char const *base = __kmp_device_env_eligible_name(e);
+ if (base == NULL)
+ break;
+ if (__kmp_env_blk_var(&block, base) != NULL)
+ continue;
+ char *all_name = __kmp_str_format("%s_ALL", base);
+ char const *all_v = __kmp_env_blk_var(&block, all_name);
+ __kmp_str_free(&all_name);
+ if (all_v != NULL) {
+ __kmp_stg_parse(base, all_v);
+ __kmp_device_env_observe_host(base, all_v);
+ }
+ }
+
// If user locks have been allocated yet, don't reset the lock vptr table.
if (!__kmp_init_user_locks) {
if (__kmp_user_lock_kind == lk_default) {
>From 7d52f329ba8396a51514829dc849f1f20a6ec698 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Mon, 4 May 2026 07:18:12 -0400
Subject: [PATCH 2/8] kmp_free
---
openmp/runtime/src/kmp_runtime.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/openmp/runtime/src/kmp_runtime.cpp b/openmp/runtime/src/kmp_runtime.cpp
index c402645af9ad6..99ee6cf909590 100644
--- a/openmp/runtime/src/kmp_runtime.cpp
+++ b/openmp/runtime/src/kmp_runtime.cpp
@@ -13,6 +13,7 @@
#include "kmp.h"
#include "kmp_affinity.h"
#include "kmp_atomic.h"
+#include "kmp_device_env.h"
#include "kmp_environment.h"
#include "kmp_error.h"
#include "kmp_i18n.h"
@@ -8328,6 +8329,8 @@ void __kmp_cleanup(void) {
__kmp_affinity_format = NULL;
}
+ __kmp_device_env_reset();
+
__kmp_i18n_catclose();
if (__kmp_nesting_nth_level)
>From e5a4efd1c0bc98ed5ab3e86ff31bf451ffba7992 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Mon, 4 May 2026 07:27:53 -0400
Subject: [PATCH 3/8] device_id_fix
---
openmp/runtime/src/kmp_device_env.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/openmp/runtime/src/kmp_device_env.cpp b/openmp/runtime/src/kmp_device_env.cpp
index b4e3b336a1894..227e3edf35188 100644
--- a/openmp/runtime/src/kmp_device_env.cpp
+++ b/openmp/runtime/src/kmp_device_env.cpp
@@ -297,14 +297,16 @@ extern "C" char const *__kmp_resolve_device_env(char const *base_name,
return NULL;
kmp_device_env_state_t *st = &__kmp_device_env_states[idx];
- if (device_id < 0) {
- // Host: <ENV> > <ENV>_ALL > default.
+ // Host: <ENV> > <ENV>_ALL
+ if (device_id == -1) {
if (st->host_value != NULL)
return st->host_value;
if (st->all_value != NULL)
return st->all_value;
return NULL;
}
+ if (device_id < 0)
+ return NULL;
// Non-host device d: <ENV>_DEV_<d> > <ENV>_DEV > <ENV>_ALL > default.
for (kmp_device_env_dev_node_t *n = st->per_device; n != NULL; n = n->next) {
>From 5e0b113e1e291083278d470feef0b61fbd7ecf82 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Mon, 4 May 2026 07:28:24 -0400
Subject: [PATCH 4/8] diag
---
openmp/runtime/src/dllexports | 1 +
openmp/runtime/src/i18n/en_US.txt | 2 ++
2 files changed, 3 insertions(+)
diff --git a/openmp/runtime/src/dllexports b/openmp/runtime/src/dllexports
index 8a70f8bc6d20c..be1b5ffb62c2b 100644
--- a/openmp/runtime/src/dllexports
+++ b/openmp/runtime/src/dllexports
@@ -556,6 +556,7 @@ kmp_set_disp_num_buffers 890
__kmpc_free
__kmpc_init_allocator
__kmpc_destroy_allocator
+ __kmpc_get_resolved_device_env
%endif
omp_set_affinity_format 748
omp_get_affinity_format 749
diff --git a/openmp/runtime/src/i18n/en_US.txt b/openmp/runtime/src/i18n/en_US.txt
index b3699df6db65d..7e078c0a83158 100644
--- a/openmp/runtime/src/i18n/en_US.txt
+++ b/openmp/runtime/src/i18n/en_US.txt
@@ -484,6 +484,8 @@ AffIgnoringNonHybrid "%1$s ignored: This machine is not a hybrid archite
AffIgnoringNotAvailable "%1$s ignored: %2$s is not available. Using \"%3$s\" instead."
SetNumTeamsInParOrTeamsRegion "%1$s() called inside a parallel or teams region; call ignored."
SetTeamsThreadLimitInParOrTeamsRegion "%1$s() called inside a parallel or teams region; call ignored."
+MalformedDeviceEnvVar "%1$s: malformed device-scope environment variable name; expected `_ALL`, `_DEV`, or `_DEV_<n>` with a non-negative integer device id; ignored."
+DeviceEnvVarOnGlobalScope "%1$s: device-scope environment variable suffix (`_ALL`/`_DEV`/`_DEV_<n>`) is not allowed on this base name (global-scope ICV or not eligible); ignored."
# --------------------------------------------------------------------------------------------------
-*- HINTS -*-
>From 0f088f5e905213a23a11ab85111045322045565f Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Tue, 12 May 2026 09:14:42 -0400
Subject: [PATCH 5/8] review_changes
---
openmp/runtime/src/i18n/en_US.txt | 2 +-
openmp/runtime/src/kmp_device_env.cpp | 22 ++++++++++
openmp/runtime/src/kmp_settings.cpp | 59 ++++++++++++++++++---------
3 files changed, 62 insertions(+), 21 deletions(-)
diff --git a/openmp/runtime/src/i18n/en_US.txt b/openmp/runtime/src/i18n/en_US.txt
index 7e078c0a83158..9bb25e85ccb8e 100644
--- a/openmp/runtime/src/i18n/en_US.txt
+++ b/openmp/runtime/src/i18n/en_US.txt
@@ -485,7 +485,7 @@ AffIgnoringNotAvailable "%1$s ignored: %2$s is not available. Using \"%3$s\
SetNumTeamsInParOrTeamsRegion "%1$s() called inside a parallel or teams region; call ignored."
SetTeamsThreadLimitInParOrTeamsRegion "%1$s() called inside a parallel or teams region; call ignored."
MalformedDeviceEnvVar "%1$s: malformed device-scope environment variable name; expected `_ALL`, `_DEV`, or `_DEV_<n>` with a non-negative integer device id; ignored."
-DeviceEnvVarOnGlobalScope "%1$s: device-scope environment variable suffix (`_ALL`/`_DEV`/`_DEV_<n>`) is not allowed on this base name (global-scope ICV or not eligible); ignored."
+DeviceEnvVarOnGlobalScope "%1$s: device-scope environment variable suffix (`_ALL`/`_DEV`/`_DEV_<n>`) is not allowed on this base name (global-scope ICV); ignored."
# --------------------------------------------------------------------------------------------------
-*- HINTS -*-
diff --git a/openmp/runtime/src/kmp_device_env.cpp b/openmp/runtime/src/kmp_device_env.cpp
index 227e3edf35188..a7c66efef1699 100644
--- a/openmp/runtime/src/kmp_device_env.cpp
+++ b/openmp/runtime/src/kmp_device_env.cpp
@@ -67,9 +67,25 @@ static int __kmp_device_env_count(void) {
return count;
}
+// Invariant: the eligible-name and denied-base tables must be disjoint --
+// the classifier short-circuits on the first eligible match and never
+// consults the denylist (see `__kmp_classify_device_env_name`). Verified
+// once-per-process in debug builds to protect future maintainers.
+static void __kmp_device_env_assert_tables_disjoint(void) {
+#ifdef KMP_DEBUG
+ for (int i = 0; __kmp_device_env_eligible_names[i] != NULL; ++i) {
+ for (int j = 0; __kmp_device_env_denied_bases[j] != NULL; ++j) {
+ KMP_DEBUG_ASSERT(strcmp(__kmp_device_env_eligible_names[i],
+ __kmp_device_env_denied_bases[j]) != 0);
+ }
+ }
+#endif
+}
+
static void __kmp_device_env_lazy_init(void) {
if (__kmp_device_env_states != NULL)
return;
+ __kmp_device_env_assert_tables_disjoint();
int count = __kmp_device_env_count();
__kmp_device_env_states = (kmp_device_env_state_t *)KMP_INTERNAL_MALLOC(
sizeof(kmp_device_env_state_t) * count);
@@ -301,6 +317,12 @@ extern "C" char const *__kmp_resolve_device_env(char const *base_name,
if (device_id == -1) {
if (st->host_value != NULL)
return st->host_value;
+ // Defensive: after `__kmp_env_initialize` completes, `host_value` is
+ // always populated whenever `<ENV>` or `<ENV>_ALL` was set (the post-pass
+ // calls `observe_host` after replaying `_ALL`). This `all_value` fallback
+ // covers the narrow window of an early query during init bootstrap (e.g.
+ // a query issued from inside `__kmp_stg_parse` while the post-pass has
+ // not yet run). Kept deliberately so the contract holds end-to-end.
if (st->all_value != NULL)
return st->all_value;
return NULL;
diff --git a/openmp/runtime/src/kmp_settings.cpp b/openmp/runtime/src/kmp_settings.cpp
index 63cb6b885f9e8..fa4237f5f7b21 100644
--- a/openmp/runtime/src/kmp_settings.cpp
+++ b/openmp/runtime/src/kmp_settings.cpp
@@ -6084,6 +6084,24 @@ static void __kmp_print_affinity_settings(const kmp_affinity_t *affinity) {
}
#endif
+// If `base_name` is on the device-scope eligible list, return the value of
+// `<base_name>_ALL` from `block` (or NULL). Returns NULL for non-eligible
+// names.
+static char const *__kmp_aux_env_blk_all_fallback(kmp_env_blk_t *block,
+ char const *base_name) {
+ for (int e = 0;; ++e) {
+ char const *base = __kmp_device_env_eligible_name(e);
+ if (base == NULL)
+ return NULL;
+ if (strcmp(base, base_name) != 0)
+ continue;
+ char *all_name = __kmp_str_format("%s_ALL", base_name);
+ char const *value = __kmp_env_blk_var(block, all_name);
+ __kmp_str_free(&all_name);
+ return value;
+ }
+}
+
static void __kmp_aux_env_initialize(kmp_env_blk_t *block) {
char const *value;
@@ -6091,7 +6109,7 @@ static void __kmp_aux_env_initialize(kmp_env_blk_t *block) {
/* OMP_NUM_THREADS */
value = __kmp_env_blk_var(block, "OMP_NUM_THREADS");
if (value == NULL)
- value = __kmp_env_blk_var(block, "OMP_NUM_THREADS_ALL");
+ value = __kmp_aux_env_blk_all_fallback(block, "OMP_NUM_THREADS");
if (value) {
ompc_set_num_threads(__kmp_dflt_team_nth);
}
@@ -6136,25 +6154,6 @@ void __kmp_env_initialize(char const *string) {
}
__kmp_env_blk_init(&block, string);
- // Device-scope env-var pre-pass: route `<ENV>_ALL`,
- // `<ENV>_DEV`, `<ENV>_DEV_<d>` into the device-scope registry.
- for (i = 0; i < block.count; ++i) {
- if ((block.vars[i].name == NULL) || (*block.vars[i].name == '\0')) {
- continue;
- }
- if (block.vars[i].value == NULL) {
- continue;
- }
- if (__kmp_device_env_record(block.vars[i].name, block.vars[i].value))
- continue;
- __kmp_device_env_observe_host(block.vars[i].name, block.vars[i].value);
-
- kmp_setting_t *setting = __kmp_stg_find(block.vars[i].name);
- if (setting != NULL) {
- setting->set = 1;
- }
- }
-
// We need to know if blocktime was set when processing OMP_WAIT_POLICY
blocktime_str = __kmp_env_blk_var(&block, "KMP_BLOCKTIME");
@@ -6223,6 +6222,26 @@ void __kmp_env_initialize(char const *string) {
#endif /* KMP_AFFINITY_SUPPORTED */
+ // Deliberately placed AFTER the KMP_WARNINGS special case above so that any
+ // warnings the pre-pass emits (MalformedDeviceEnvVar /
+ // DeviceEnvVarOnGlobalScope) honour the user-requested warning level.
+ for (i = 0; i < block.count; ++i) {
+ if ((block.vars[i].name == NULL) || (*block.vars[i].name == '\0')) {
+ continue;
+ }
+ if (block.vars[i].value == NULL) {
+ continue;
+ }
+ if (__kmp_device_env_record(block.vars[i].name, block.vars[i].value))
+ continue;
+ __kmp_device_env_observe_host(block.vars[i].name, block.vars[i].value);
+
+ kmp_setting_t *setting = __kmp_stg_find(block.vars[i].name);
+ if (setting != NULL) {
+ setting->set = 1;
+ }
+ }
+
// Set up the nested proc bind type vector.
if (__kmp_nested_proc_bind.bind_types == NULL) {
__kmp_nested_proc_bind.bind_types =
>From 8f7ad8da0a7546bc75c6660abb561110f40a75f4 Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Thu, 11 Jun 2026 09:01:25 -0400
Subject: [PATCH 6/8] tests
---
openmp/runtime/src/kmp_device_env.cpp | 3 +-
.../env/omp_default_device_all_rejected.c | 22 ++++++++++
.../test/env/omp_denylist_extra_coverage.c | 26 +++++++++++
.../test/env/omp_num_threads_all_demo.c | 25 +++++++++++
.../test/env/omp_num_threads_all_host.c | 16 +++++++
.../env/omp_num_threads_all_set_defaults.c | 39 +++++++++++++++++
.../env/omp_num_threads_dev_all_fallback.c | 32 ++++++++++++++
.../test/env/omp_num_threads_dev_invalid.c | 22 ++++++++++
.../test/env/omp_num_threads_dev_overflow.c | 38 ++++++++++++++++
.../test/env/omp_num_threads_dev_resolve.c | 43 +++++++++++++++++++
.../env/omp_num_threads_host_overrides_all.c | 33 ++++++++++++++
.../test/env/omp_num_threads_warnings_off.c | 33 ++++++++++++++
12 files changed, 330 insertions(+), 2 deletions(-)
create mode 100644 openmp/runtime/test/env/omp_default_device_all_rejected.c
create mode 100644 openmp/runtime/test/env/omp_denylist_extra_coverage.c
create mode 100644 openmp/runtime/test/env/omp_num_threads_all_demo.c
create mode 100644 openmp/runtime/test/env/omp_num_threads_all_host.c
create mode 100644 openmp/runtime/test/env/omp_num_threads_all_set_defaults.c
create mode 100644 openmp/runtime/test/env/omp_num_threads_dev_all_fallback.c
create mode 100644 openmp/runtime/test/env/omp_num_threads_dev_invalid.c
create mode 100644 openmp/runtime/test/env/omp_num_threads_dev_overflow.c
create mode 100644 openmp/runtime/test/env/omp_num_threads_dev_resolve.c
create mode 100644 openmp/runtime/test/env/omp_num_threads_host_overrides_all.c
create mode 100644 openmp/runtime/test/env/omp_num_threads_warnings_off.c
diff --git a/openmp/runtime/src/kmp_device_env.cpp b/openmp/runtime/src/kmp_device_env.cpp
index a7c66efef1699..4238e81803c9f 100644
--- a/openmp/runtime/src/kmp_device_env.cpp
+++ b/openmp/runtime/src/kmp_device_env.cpp
@@ -7,8 +7,7 @@
//===----------------------------------------------------------------------===//
//
// OpenMP 6.0 device-scope env-var registry. See kmp_device_env.h for the
-// public-internal contract; see openmp/docs/OpenMP_6_0_DeviceScope_EnvVars_
-// Walkthrough.md for the design notes and examples.
+// public-internal contract;
//
//===----------------------------------------------------------------------===//
diff --git a/openmp/runtime/test/env/omp_default_device_all_rejected.c b/openmp/runtime/test/env/omp_default_device_all_rejected.c
new file mode 100644
index 0000000000000..3d869ff5bb235
--- /dev/null
+++ b/openmp/runtime/test/env/omp_default_device_all_rejected.c
@@ -0,0 +1,22 @@
+// RUN: %libomp-compile
+// RUN: env KMP_WARNINGS=1 OMP_DEFAULT_DEVICE_ALL=2 %libomp-run 2>&1 | FileCheck %s
+//
+// Suffix on global-scope base must be rejected with a warning AND not
+// reach the ICV.
+
+#include <omp.h>
+#include <stdio.h>
+
+int main(void) {
+ (void)omp_get_max_threads();
+ int dd = omp_get_default_device();
+ if (dd == 2) {
+ fprintf(stderr, "FAIL: OMP_DEFAULT_DEVICE_ALL=2 was applied; got %d\n", dd);
+ return 1;
+ }
+ printf("DONE\n");
+ return 0;
+}
+
+// CHECK: {{^OMP: Warning #[0-9]+:.*OMP_DEFAULT_DEVICE_ALL.*}}
+// CHECK: DONE
diff --git a/openmp/runtime/test/env/omp_denylist_extra_coverage.c b/openmp/runtime/test/env/omp_denylist_extra_coverage.c
new file mode 100644
index 0000000000000..6eb68acf63273
--- /dev/null
+++ b/openmp/runtime/test/env/omp_denylist_extra_coverage.c
@@ -0,0 +1,26 @@
+// RUN: %libomp-compile
+// RUN: env KMP_WARNINGS=1 OMP_TARGET_OFFLOAD_DEV_0=mandatory \
+// RUN: OMP_TOOL_LIBRARIES_ALL=libfoo.so \
+// RUN: OMP_CANCELLATION_DEV=true \
+// RUN: OMP_NUM_THREADS_ALL=8 \
+// RUN: %libomp-run 2>&1 | FileCheck %s
+//
+// Denylist: suffixed global OMP_* warns; OMP_NUM_THREADS_ALL still applies.
+
+#include <omp.h>
+#include <stdio.h>
+
+int main(void) {
+ int max = omp_get_max_threads();
+ if (max != 8) {
+ fprintf(stderr, "FAIL: omp_get_max_threads()=%d, expected 8\n", max);
+ return 1;
+ }
+ printf("DONE\n");
+ return 0;
+}
+
+// CHECK-DAG: {{^OMP: Warning #[0-9]+:.*OMP_TARGET_OFFLOAD_DEV_0.*}}
+// CHECK-DAG: {{^OMP: Warning #[0-9]+:.*OMP_TOOL_LIBRARIES_ALL.*}}
+// CHECK-DAG: {{^OMP: Warning #[0-9]+:.*OMP_CANCELLATION_DEV.*}}
+// CHECK: DONE
diff --git a/openmp/runtime/test/env/omp_num_threads_all_demo.c b/openmp/runtime/test/env/omp_num_threads_all_demo.c
new file mode 100644
index 0000000000000..fd3e9aacb668f
--- /dev/null
+++ b/openmp/runtime/test/env/omp_num_threads_all_demo.c
@@ -0,0 +1,25 @@
+// RUN: %libomp-compile
+// RUN: env OMP_NUM_THREADS_ALL=8 OMP_NUM_THREADS_DEV_0=128 %libomp-run | FileCheck %s
+//
+// Demo: host=8 (_ALL), device 0=128 (_DEV_0), other devices=8 (_ALL fallback).
+
+#include <omp.h>
+#include <stdio.h>
+
+extern const char *__kmpc_get_resolved_device_env(const char *name,
+ int device_id);
+
+int main(void) {
+ int host_max = omp_get_max_threads();
+ printf("host omp_get_max_threads() = %d\n", host_max);
+ for (int d = 0; d < 3; ++d) {
+ const char *v = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", d);
+ printf("device %d resolved OMP_NUM_THREADS = %s\n", d, v ? v : "(default)");
+ }
+ return 0;
+}
+
+// CHECK: host omp_get_max_threads() = 8
+// CHECK: device 0 resolved OMP_NUM_THREADS = 128
+// CHECK: device 1 resolved OMP_NUM_THREADS = 8
+// CHECK: device 2 resolved OMP_NUM_THREADS = 8
diff --git a/openmp/runtime/test/env/omp_num_threads_all_host.c b/openmp/runtime/test/env/omp_num_threads_all_host.c
new file mode 100644
index 0000000000000..93694a5fb45a9
--- /dev/null
+++ b/openmp/runtime/test/env/omp_num_threads_all_host.c
@@ -0,0 +1,16 @@
+// RUN: %libomp-compile && env OMP_NUM_THREADS_ALL=8 %libomp-run
+//
+// OpenMP 6.0: host falls through to `_ALL` when `<ENV>` is unset.
+
+#include <omp.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+ int max = omp_get_max_threads();
+ if (max != 8) {
+ fprintf(stderr, "FAIL: omp_get_max_threads()=%d, expected 8\n", max);
+ return 1;
+ }
+ return 0;
+}
diff --git a/openmp/runtime/test/env/omp_num_threads_all_set_defaults.c b/openmp/runtime/test/env/omp_num_threads_all_set_defaults.c
new file mode 100644
index 0000000000000..3f18a96d6b915
--- /dev/null
+++ b/openmp/runtime/test/env/omp_num_threads_all_set_defaults.c
@@ -0,0 +1,39 @@
+// RUN: %libomp-compile-and-run
+//
+// kmp_set_defaults("OMP_NUM_THREADS_ALL=N") must propagate to live threads,
+// and an unrelated kmp_set_defaults call must not wipe registry state.
+
+#include <omp.h>
+#include <stdio.h>
+
+extern void kmp_set_defaults(const char *);
+extern const char *__kmpc_get_resolved_device_env(const char *name,
+ int device_id);
+
+int main(void) {
+ int rc = 0;
+ (void)omp_get_max_threads();
+
+ kmp_set_defaults("OMP_NUM_THREADS_ALL=16");
+ if (omp_get_max_threads() != 16) {
+ fprintf(stderr, "FAIL: after _ALL=16 expected 16 got %d\n",
+ omp_get_max_threads());
+ rc = 1;
+ }
+ const char *q = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", -1);
+ if (q == NULL || q[0] != '1' || q[1] != '6' || q[2] != '\0') {
+ fprintf(stderr, "FAIL: query host expected '16' got %s\n",
+ q ? q : "(null)");
+ rc = 1;
+ }
+
+ kmp_set_defaults("KMP_BLOCKTIME=200");
+ q = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", 0);
+ if (q == NULL || q[0] != '1' || q[1] != '6' || q[2] != '\0') {
+ fprintf(stderr,
+ "FAIL: registry wiped by unrelated kmp_set_defaults; got %s\n",
+ q ? q : "(null)");
+ rc = 1;
+ }
+ return rc;
+}
diff --git a/openmp/runtime/test/env/omp_num_threads_dev_all_fallback.c b/openmp/runtime/test/env/omp_num_threads_dev_all_fallback.c
new file mode 100644
index 0000000000000..395e021df6a8d
--- /dev/null
+++ b/openmp/runtime/test/env/omp_num_threads_dev_all_fallback.c
@@ -0,0 +1,32 @@
+// RUN: %libomp-compile && env OMP_NUM_THREADS_ALL=8 %libomp-run
+//
+// OpenMP 6.0: every non-host device falls through to `_ALL` when no
+// `_DEV[_d]` is set.
+
+#include <omp.h>
+#include <stdio.h>
+#include <string.h>
+
+extern const char *__kmpc_get_resolved_device_env(const char *name,
+ int device_id);
+
+static int check(int device_id, const char *expect) {
+ const char *got =
+ __kmpc_get_resolved_device_env("OMP_NUM_THREADS", device_id);
+ if (!got || strcmp(got, expect) != 0) {
+ fprintf(stderr, "FAIL: device_id=%d got '%s' expected '%s'\n", device_id,
+ got ? got : "(null)", expect);
+ return 1;
+ }
+ return 0;
+}
+
+int main(void) {
+ (void)omp_get_max_threads();
+ int rc = 0;
+ rc |= check(-1, "8"); // host
+ rc |= check(0, "8");
+ rc |= check(1, "8");
+ rc |= check(2, "8");
+ return rc;
+}
diff --git a/openmp/runtime/test/env/omp_num_threads_dev_invalid.c b/openmp/runtime/test/env/omp_num_threads_dev_invalid.c
new file mode 100644
index 0000000000000..93dbb82d3eb18
--- /dev/null
+++ b/openmp/runtime/test/env/omp_num_threads_dev_invalid.c
@@ -0,0 +1,22 @@
+// RUN: %libomp-compile
+// RUN: env KMP_WARNINGS=1 OMP_NUM_THREADS_DEV_abc=10 OMP_NUM_THREADS_ALL=4 \
+// RUN: %libomp-run 2>&1 | FileCheck %s
+//
+// Malformed `_DEV_<token>` is rejected with a warning, and a sibling
+// valid `_ALL` setting is unaffected.
+
+#include <omp.h>
+#include <stdio.h>
+
+int main(void) {
+ int max = omp_get_max_threads();
+ if (max != 4) {
+ fprintf(stderr, "FAIL: omp_get_max_threads()=%d, expected 4\n", max);
+ return 1;
+ }
+ printf("DONE\n");
+ return 0;
+}
+
+// CHECK: {{^OMP: Warning #[0-9]+:.*OMP_NUM_THREADS_DEV_abc.*}}
+// CHECK: DONE
diff --git a/openmp/runtime/test/env/omp_num_threads_dev_overflow.c b/openmp/runtime/test/env/omp_num_threads_dev_overflow.c
new file mode 100644
index 0000000000000..00d77fd24d1af
--- /dev/null
+++ b/openmp/runtime/test/env/omp_num_threads_dev_overflow.c
@@ -0,0 +1,38 @@
+// RUN: %libomp-compile
+// RUN: env KMP_WARNINGS=1 OMP_NUM_THREADS_DEV_2147483647=10 \
+// RUN: OMP_NUM_THREADS_DEV_99999999999=20 \
+// RUN: OMP_NUM_THREADS_DEV0=30 OMP_NUM_THREADS_ALLEY=40 \
+// RUN: OMP_NUM_THREADS_ALL=4 \
+// RUN: %libomp-run 2>&1 | FileCheck %s
+//
+// (a) `_DEV_<huge>` rejected with a warning;
+// (b) similar-looking names (`_DEV0`, `_ALLEY`) silently ignored, no warning;
+// (c) sibling valid `_ALL=4` still applies.
+
+#include <omp.h>
+#include <stdio.h>
+#include <string.h>
+
+extern const char *__kmpc_get_resolved_device_env(const char *name,
+ int device_id);
+
+int main(void) {
+ if (omp_get_max_threads() != 4) {
+ fprintf(stderr, "FAIL: host got %d, expected 4\n", omp_get_max_threads());
+ return 1;
+ }
+ const char *q = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", 2147483646);
+ if (q == NULL || strcmp(q, "4") != 0) {
+ fprintf(stderr, "FAIL: large valid id expected '4', got %s\n",
+ q ? q : "(null)");
+ return 1;
+ }
+ printf("DONE\n");
+ return 0;
+}
+
+// CHECK: {{^OMP: Warning #[0-9]+:.*OMP_NUM_THREADS_DEV_2147483647.*}}
+// CHECK: {{^OMP: Warning #[0-9]+:.*OMP_NUM_THREADS_DEV_99999999999.*}}
+// CHECK-NOT: {{^OMP: Warning #[0-9]+:.*OMP_NUM_THREADS_DEV0.*}}
+// CHECK-NOT: {{^OMP: Warning #[0-9]+:.*OMP_NUM_THREADS_ALLEY.*}}
+// CHECK: DONE
diff --git a/openmp/runtime/test/env/omp_num_threads_dev_resolve.c b/openmp/runtime/test/env/omp_num_threads_dev_resolve.c
new file mode 100644
index 0000000000000..4ab49db0372c3
--- /dev/null
+++ b/openmp/runtime/test/env/omp_num_threads_dev_resolve.c
@@ -0,0 +1,43 @@
+// RUN: %libomp-compile && env OMP_NUM_THREADS_ALL=8 OMP_NUM_THREADS_DEV=64
+// OMP_NUM_THREADS_DEV_0=128 %libomp-run
+//
+// OpenMP 6.0 non-host precedence: `_DEV_<d>` > `_DEV` > `_ALL` > default.
+
+#include <omp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern const char *__kmpc_get_resolved_device_env(const char *name,
+ int device_id);
+
+static int check(const char *name, int device_id, const char *expect) {
+ const char *got = __kmpc_get_resolved_device_env(name, device_id);
+ if (got == NULL) {
+ fprintf(stderr, "FAIL: %s device_id=%d resolved to NULL, expected %s\n",
+ name, device_id, expect);
+ return 1;
+ }
+ if (strcmp(got, expect) != 0) {
+ fprintf(stderr, "FAIL: %s device_id=%d resolved to '%s', expected '%s'\n",
+ name, device_id, got, expect);
+ return 1;
+ }
+ return 0;
+}
+
+int main(void) {
+ int rc = 0;
+ (void)omp_get_max_threads();
+ rc |= check("OMP_NUM_THREADS", -1, "8"); // host: _ALL
+ rc |= check("OMP_NUM_THREADS", 0, "128"); // _DEV_0 wins
+ rc |= check("OMP_NUM_THREADS", 1, "64"); // _DEV
+ rc |= check("OMP_NUM_THREADS", 2, "64"); // _DEV (no _DEV_2)
+
+ // Strict host-sentinel contract: only -1 is host.
+ if (__kmpc_get_resolved_device_env("OMP_NUM_THREADS", -2) != NULL) {
+ fprintf(stderr, "FAIL: device_id=-2 must return NULL\n");
+ rc = 1;
+ }
+ return rc;
+}
diff --git a/openmp/runtime/test/env/omp_num_threads_host_overrides_all.c b/openmp/runtime/test/env/omp_num_threads_host_overrides_all.c
new file mode 100644
index 0000000000000..b6e90b5a368bf
--- /dev/null
+++ b/openmp/runtime/test/env/omp_num_threads_host_overrides_all.c
@@ -0,0 +1,33 @@
+// RUN: %libomp-compile && env OMP_NUM_THREADS=4 OMP_NUM_THREADS_ALL=8
+// %libomp-run
+//
+// Host OMP_NUM_THREADS beats _ALL; legacy ICV and query API agree.
+
+#include <omp.h>
+#include <stdio.h>
+#include <string.h>
+
+extern const char *__kmpc_get_resolved_device_env(const char *name,
+ int device_id);
+
+int main(void) {
+ int max = omp_get_max_threads();
+ if (max != 4) {
+ fprintf(stderr, "FAIL: omp_get_max_threads()=%d, expected 4\n", max);
+ return 1;
+ }
+ const char *host = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", -1);
+ if (host == NULL || strcmp(host, "4") != 0) {
+ fprintf(stderr, "FAIL: host query expected '4' got '%s'\n",
+ host ? host : "(null)");
+ return 1;
+ }
+ // Sibling `_ALL` must still resolve for non-host devices.
+ const char *dev0 = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", 0);
+ if (dev0 == NULL || strcmp(dev0, "8") != 0) {
+ fprintf(stderr, "FAIL: dev 0 query expected '8' got '%s'\n",
+ dev0 ? dev0 : "(null)");
+ return 1;
+ }
+ return 0;
+}
diff --git a/openmp/runtime/test/env/omp_num_threads_warnings_off.c b/openmp/runtime/test/env/omp_num_threads_warnings_off.c
new file mode 100644
index 0000000000000..6b8b631ec4fba
--- /dev/null
+++ b/openmp/runtime/test/env/omp_num_threads_warnings_off.c
@@ -0,0 +1,33 @@
+// RUN: %libomp-compile
+// RUN: env KMP_WARNINGS=0 OMP_NUM_THREADS_DEV_abc=10 \
+// RUN: OMP_DEFAULT_DEVICE_ALL=2 OMP_NUM_THREADS_ALL=8 \
+// RUN: %libomp-run 2>&1 | FileCheck %s
+//
+// KMP_WARNINGS=0 silences device-scope warnings; OMP_NUM_THREADS_ALL=8 applies.
+
+#include <omp.h>
+#include <stdio.h>
+#include <string.h>
+
+extern const char *__kmpc_get_resolved_device_env(const char *name,
+ int device_id);
+
+int main(void) {
+ int max = omp_get_max_threads();
+ if (max != 8) {
+ fprintf(stderr, "FAIL: omp_get_max_threads()=%d, expected 8\n", max);
+ return 1;
+ }
+ const char *host = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", -1);
+ if (host == NULL || strcmp(host, "8") != 0) {
+ fprintf(stderr, "FAIL: host query expected '8' got '%s'\n",
+ host ? host : "(null)");
+ return 1;
+ }
+ printf("DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: {{^OMP: Warning #[0-9]+:.*OMP_NUM_THREADS_DEV_abc.*}}
+// CHECK-NOT: {{^OMP: Warning #[0-9]+:.*OMP_DEFAULT_DEVICE_ALL.*}}
+// CHECK: DONE
>From c84b5ab6afcd7030ec2f62b9795fd7d07a361e7c Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Thu, 11 Jun 2026 08:59:33 -0400
Subject: [PATCH 7/8] run-lines-indent
---
openmp/runtime/test/env/omp_default_device_all_rejected.c | 3 ++-
openmp/runtime/test/env/omp_num_threads_all_demo.c | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/openmp/runtime/test/env/omp_default_device_all_rejected.c b/openmp/runtime/test/env/omp_default_device_all_rejected.c
index 3d869ff5bb235..4fa11f1e91cae 100644
--- a/openmp/runtime/test/env/omp_default_device_all_rejected.c
+++ b/openmp/runtime/test/env/omp_default_device_all_rejected.c
@@ -1,5 +1,6 @@
// RUN: %libomp-compile
-// RUN: env KMP_WARNINGS=1 OMP_DEFAULT_DEVICE_ALL=2 %libomp-run 2>&1 | FileCheck %s
+// RUN: env KMP_WARNINGS=1 OMP_DEFAULT_DEVICE_ALL=2 %libomp-run 2>&1 \
+// RUN: | FileCheck %s
//
// Suffix on global-scope base must be rejected with a warning AND not
// reach the ICV.
diff --git a/openmp/runtime/test/env/omp_num_threads_all_demo.c b/openmp/runtime/test/env/omp_num_threads_all_demo.c
index fd3e9aacb668f..eba36b6d7b8a2 100644
--- a/openmp/runtime/test/env/omp_num_threads_all_demo.c
+++ b/openmp/runtime/test/env/omp_num_threads_all_demo.c
@@ -1,5 +1,6 @@
// RUN: %libomp-compile
-// RUN: env OMP_NUM_THREADS_ALL=8 OMP_NUM_THREADS_DEV_0=128 %libomp-run | FileCheck %s
+// RUN: env OMP_NUM_THREADS_ALL=8 OMP_NUM_THREADS_DEV_0=128 %libomp-run \
+// RUN: | FileCheck %s
//
// Demo: host=8 (_ALL), device 0=128 (_DEV_0), other devices=8 (_ALL fallback).
>From f399b65f3eb24b123959f4ec8fa37c72cb72984f Mon Sep 17 00:00:00 2001
From: amtiwari <amtiwari at amd.com>
Date: Mon, 22 Jun 2026 05:51:38 -0400
Subject: [PATCH 8/8] resolve_kmpc_add_helpers
---
openmp/runtime/src/dllexports | 1 -
openmp/runtime/src/exports_so.txt | 2 +
openmp/runtime/src/kmp_device_env.cpp | 53 +++++++++++--------
openmp/runtime/src/kmp_device_env.h | 13 +++--
.../test/env/omp_num_threads_all_demo.c | 5 +-
.../env/omp_num_threads_all_set_defaults.c | 8 +--
.../env/omp_num_threads_dev_all_fallback.c | 16 +++---
.../test/env/omp_num_threads_dev_overflow.c | 5 +-
.../test/env/omp_num_threads_dev_resolve.c | 38 +++++--------
.../env/omp_num_threads_host_overrides_all.c | 12 ++---
.../test/env/omp_num_threads_host_vs_device.c | 31 +++++++++++
.../test/env/omp_num_threads_warnings_off.c | 5 +-
12 files changed, 109 insertions(+), 80 deletions(-)
create mode 100644 openmp/runtime/test/env/omp_num_threads_host_vs_device.c
diff --git a/openmp/runtime/src/dllexports b/openmp/runtime/src/dllexports
index be1b5ffb62c2b..8a70f8bc6d20c 100644
--- a/openmp/runtime/src/dllexports
+++ b/openmp/runtime/src/dllexports
@@ -556,7 +556,6 @@ kmp_set_disp_num_buffers 890
__kmpc_free
__kmpc_init_allocator
__kmpc_destroy_allocator
- __kmpc_get_resolved_device_env
%endif
omp_set_affinity_format 748
omp_get_affinity_format 749
diff --git a/openmp/runtime/src/exports_so.txt b/openmp/runtime/src/exports_so.txt
index d826882d98804..f036c9c2d23e6 100644
--- a/openmp/runtime/src/exports_so.txt
+++ b/openmp/runtime/src/exports_so.txt
@@ -53,6 +53,8 @@ VERSION {
__kmp_thread_pool;
__kmp_reset_stats;
+ __kmp_resolve_host_env;
+ __kmp_resolve_device_env;
#if USE_ITT_BUILD
#
diff --git a/openmp/runtime/src/kmp_device_env.cpp b/openmp/runtime/src/kmp_device_env.cpp
index 4238e81803c9f..5bf030f4e7433 100644
--- a/openmp/runtime/src/kmp_device_env.cpp
+++ b/openmp/runtime/src/kmp_device_env.cpp
@@ -305,29 +305,43 @@ extern "C" int __kmp_device_env_record(char const *full_name,
return 0;
}
-extern "C" char const *__kmp_resolve_device_env(char const *base_name,
- int device_id) {
+// Look up the registry state for `base_name`, or NULL if it is not an
+// eligible base or nothing was recorded yet.
+static kmp_device_env_state_t *__kmp_device_env_lookup(char const *base_name) {
int idx = __kmp_device_env_index(base_name);
if (idx < 0 || __kmp_device_env_states == NULL)
return NULL;
- kmp_device_env_state_t *st = &__kmp_device_env_states[idx];
+ return &__kmp_device_env_states[idx];
+}
- // Host: <ENV> > <ENV>_ALL
- if (device_id == -1) {
- if (st->host_value != NULL)
- return st->host_value;
- // Defensive: after `__kmp_env_initialize` completes, `host_value` is
- // always populated whenever `<ENV>` or `<ENV>_ALL` was set (the post-pass
- // calls `observe_host` after replaying `_ALL`). This `all_value` fallback
- // covers the narrow window of an early query during init bootstrap (e.g.
- // a query issued from inside `__kmp_stg_parse` while the post-pass has
- // not yet run). Kept deliberately so the contract holds end-to-end.
- if (st->all_value != NULL)
- return st->all_value;
+extern "C" char const *__kmp_resolve_host_env(char const *base_name) {
+ kmp_device_env_state_t *st = __kmp_device_env_lookup(base_name);
+ if (st == NULL)
return NULL;
- }
+ // Host: <ENV> > <ENV>_ALL.
+ if (st->host_value != NULL)
+ return st->host_value;
+ // Defensive: after `__kmp_env_initialize` completes, `host_value` is
+ // always populated whenever `<ENV>` or `<ENV>_ALL` was set (the post-pass
+ // calls `observe_host` after replaying `_ALL`). This `all_value` fallback
+ // covers the narrow window of an early query during init bootstrap (e.g.
+ // a query issued from inside `__kmp_stg_parse` while the post-pass has
+ // not yet run). Kept deliberately so the contract holds end-to-end.
+ if (st->all_value != NULL)
+ return st->all_value;
+ return NULL;
+}
+
+extern "C" char const *__kmp_resolve_device_env(char const *base_name,
+ int device_id) {
+ // Host queries must use `__kmp_resolve_host_env`; this entry point is for
+ // non-host devices only and takes a 0-based, non-negative device id.
+ KMP_DEBUG_ASSERT(device_id >= 0);
if (device_id < 0)
return NULL;
+ kmp_device_env_state_t *st = __kmp_device_env_lookup(base_name);
+ if (st == NULL)
+ return NULL;
// Non-host device d: <ENV>_DEV_<d> > <ENV>_DEV > <ENV>_ALL > default.
for (kmp_device_env_dev_node_t *n = st->per_device; n != NULL; n = n->next) {
@@ -371,10 +385,3 @@ extern "C" void __kmp_device_env_reset(void) {
KMP_INTERNAL_FREE(__kmp_device_env_states);
__kmp_device_env_states = NULL;
}
-
-// Public test/query helper. Returns the resolved value for `name` on
-// `device_id`.
-extern "C" char const *__kmpc_get_resolved_device_env(char const *name,
- int device_id) {
- return __kmp_resolve_device_env(name, device_id);
-}
diff --git a/openmp/runtime/src/kmp_device_env.h b/openmp/runtime/src/kmp_device_env.h
index e40b61743daab..eeb6d4c6b613c 100644
--- a/openmp/runtime/src/kmp_device_env.h
+++ b/openmp/runtime/src/kmp_device_env.h
@@ -46,10 +46,15 @@ extern "C" {
// 0 -- not a device-scope variant; caller continues normal processing.
int __kmp_device_env_record(char const *full_name, char const *value);
-// Resolve the effective string for `base_name` on `device_id` per the
-// precedence rules above. Pass `device_id == -1` to request the host;
-// any other negative value returns NULL (defensive). Returns NULL when no
-// source applies (caller falls back to its own default).
+// Resolve the effective string for `base_name` on the host device:
+// <ENV> > <ENV>_ALL > NULL.
+// Host and device queries are intentionally separate functions so a caller
+// can never accidentally pass a non-host device id (or vice versa).
+char const *__kmp_resolve_host_env(char const *base_name);
+
+// Resolve the effective string for `base_name` on a non-host device:
+// <ENV>_DEV_<d> > <ENV>_DEV > <ENV>_ALL > NULL.
+// `device_id` is 0-based and must be >= 0.
char const *__kmp_resolve_device_env(char const *base_name, int device_id);
// Record an unsuffixed `<ENV>=value` pair so the host query is consistent
diff --git a/openmp/runtime/test/env/omp_num_threads_all_demo.c b/openmp/runtime/test/env/omp_num_threads_all_demo.c
index eba36b6d7b8a2..258e3be92b380 100644
--- a/openmp/runtime/test/env/omp_num_threads_all_demo.c
+++ b/openmp/runtime/test/env/omp_num_threads_all_demo.c
@@ -7,14 +7,13 @@
#include <omp.h>
#include <stdio.h>
-extern const char *__kmpc_get_resolved_device_env(const char *name,
- int device_id);
+extern const char *__kmp_resolve_device_env(const char *name, int device_id);
int main(void) {
int host_max = omp_get_max_threads();
printf("host omp_get_max_threads() = %d\n", host_max);
for (int d = 0; d < 3; ++d) {
- const char *v = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", d);
+ const char *v = __kmp_resolve_device_env("OMP_NUM_THREADS", d);
printf("device %d resolved OMP_NUM_THREADS = %s\n", d, v ? v : "(default)");
}
return 0;
diff --git a/openmp/runtime/test/env/omp_num_threads_all_set_defaults.c b/openmp/runtime/test/env/omp_num_threads_all_set_defaults.c
index 3f18a96d6b915..923b78b47cf48 100644
--- a/openmp/runtime/test/env/omp_num_threads_all_set_defaults.c
+++ b/openmp/runtime/test/env/omp_num_threads_all_set_defaults.c
@@ -7,8 +7,8 @@
#include <stdio.h>
extern void kmp_set_defaults(const char *);
-extern const char *__kmpc_get_resolved_device_env(const char *name,
- int device_id);
+extern const char *__kmp_resolve_host_env(const char *name);
+extern const char *__kmp_resolve_device_env(const char *name, int device_id);
int main(void) {
int rc = 0;
@@ -20,7 +20,7 @@ int main(void) {
omp_get_max_threads());
rc = 1;
}
- const char *q = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", -1);
+ const char *q = __kmp_resolve_host_env("OMP_NUM_THREADS");
if (q == NULL || q[0] != '1' || q[1] != '6' || q[2] != '\0') {
fprintf(stderr, "FAIL: query host expected '16' got %s\n",
q ? q : "(null)");
@@ -28,7 +28,7 @@ int main(void) {
}
kmp_set_defaults("KMP_BLOCKTIME=200");
- q = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", 0);
+ q = __kmp_resolve_device_env("OMP_NUM_THREADS", 0);
if (q == NULL || q[0] != '1' || q[1] != '6' || q[2] != '\0') {
fprintf(stderr,
"FAIL: registry wiped by unrelated kmp_set_defaults; got %s\n",
diff --git a/openmp/runtime/test/env/omp_num_threads_dev_all_fallback.c b/openmp/runtime/test/env/omp_num_threads_dev_all_fallback.c
index 395e021df6a8d..bf1c83178825d 100644
--- a/openmp/runtime/test/env/omp_num_threads_dev_all_fallback.c
+++ b/openmp/runtime/test/env/omp_num_threads_dev_all_fallback.c
@@ -7,12 +7,10 @@
#include <stdio.h>
#include <string.h>
-extern const char *__kmpc_get_resolved_device_env(const char *name,
- int device_id);
+extern const char *__kmp_resolve_host_env(const char *name);
+extern const char *__kmp_resolve_device_env(const char *name, int device_id);
-static int check(int device_id, const char *expect) {
- const char *got =
- __kmpc_get_resolved_device_env("OMP_NUM_THREADS", device_id);
+static int check(int device_id, const char *got, const char *expect) {
if (!got || strcmp(got, expect) != 0) {
fprintf(stderr, "FAIL: device_id=%d got '%s' expected '%s'\n", device_id,
got ? got : "(null)", expect);
@@ -24,9 +22,9 @@ static int check(int device_id, const char *expect) {
int main(void) {
(void)omp_get_max_threads();
int rc = 0;
- rc |= check(-1, "8"); // host
- rc |= check(0, "8");
- rc |= check(1, "8");
- rc |= check(2, "8");
+ rc |= check(-1, __kmp_resolve_host_env("OMP_NUM_THREADS"), "8"); // host
+ rc |= check(0, __kmp_resolve_device_env("OMP_NUM_THREADS", 0), "8");
+ rc |= check(1, __kmp_resolve_device_env("OMP_NUM_THREADS", 1), "8");
+ rc |= check(2, __kmp_resolve_device_env("OMP_NUM_THREADS", 2), "8");
return rc;
}
diff --git a/openmp/runtime/test/env/omp_num_threads_dev_overflow.c b/openmp/runtime/test/env/omp_num_threads_dev_overflow.c
index 00d77fd24d1af..742a5bb6e7948 100644
--- a/openmp/runtime/test/env/omp_num_threads_dev_overflow.c
+++ b/openmp/runtime/test/env/omp_num_threads_dev_overflow.c
@@ -13,15 +13,14 @@
#include <stdio.h>
#include <string.h>
-extern const char *__kmpc_get_resolved_device_env(const char *name,
- int device_id);
+extern const char *__kmp_resolve_device_env(const char *name, int device_id);
int main(void) {
if (omp_get_max_threads() != 4) {
fprintf(stderr, "FAIL: host got %d, expected 4\n", omp_get_max_threads());
return 1;
}
- const char *q = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", 2147483646);
+ const char *q = __kmp_resolve_device_env("OMP_NUM_THREADS", 2147483646);
if (q == NULL || strcmp(q, "4") != 0) {
fprintf(stderr, "FAIL: large valid id expected '4', got %s\n",
q ? q : "(null)");
diff --git a/openmp/runtime/test/env/omp_num_threads_dev_resolve.c b/openmp/runtime/test/env/omp_num_threads_dev_resolve.c
index 4ab49db0372c3..2db0aaae4f2d7 100644
--- a/openmp/runtime/test/env/omp_num_threads_dev_resolve.c
+++ b/openmp/runtime/test/env/omp_num_threads_dev_resolve.c
@@ -1,5 +1,6 @@
-// RUN: %libomp-compile && env OMP_NUM_THREADS_ALL=8 OMP_NUM_THREADS_DEV=64
-// OMP_NUM_THREADS_DEV_0=128 %libomp-run
+// RUN: %libomp-compile && \
+// RUN: env OMP_NUM_THREADS_ALL=8 OMP_NUM_THREADS_DEV=64 \
+// RUN: OMP_NUM_THREADS_DEV_0=128 %libomp-run
//
// OpenMP 6.0 non-host precedence: `_DEV_<d>` > `_DEV` > `_ALL` > default.
@@ -8,19 +9,13 @@
#include <stdlib.h>
#include <string.h>
-extern const char *__kmpc_get_resolved_device_env(const char *name,
- int device_id);
+extern const char *__kmp_resolve_host_env(const char *name);
+extern const char *__kmp_resolve_device_env(const char *name, int device_id);
-static int check(const char *name, int device_id, const char *expect) {
- const char *got = __kmpc_get_resolved_device_env(name, device_id);
- if (got == NULL) {
- fprintf(stderr, "FAIL: %s device_id=%d resolved to NULL, expected %s\n",
- name, device_id, expect);
- return 1;
- }
- if (strcmp(got, expect) != 0) {
- fprintf(stderr, "FAIL: %s device_id=%d resolved to '%s', expected '%s'\n",
- name, device_id, got, expect);
+static int check(const char *got, int device_id, const char *expect) {
+ if (got == NULL || strcmp(got, expect) != 0) {
+ fprintf(stderr, "FAIL: device_id=%d resolved to '%s', expected '%s'\n",
+ device_id, got ? got : "(null)", expect);
return 1;
}
return 0;
@@ -29,15 +24,10 @@ static int check(const char *name, int device_id, const char *expect) {
int main(void) {
int rc = 0;
(void)omp_get_max_threads();
- rc |= check("OMP_NUM_THREADS", -1, "8"); // host: _ALL
- rc |= check("OMP_NUM_THREADS", 0, "128"); // _DEV_0 wins
- rc |= check("OMP_NUM_THREADS", 1, "64"); // _DEV
- rc |= check("OMP_NUM_THREADS", 2, "64"); // _DEV (no _DEV_2)
-
- // Strict host-sentinel contract: only -1 is host.
- if (__kmpc_get_resolved_device_env("OMP_NUM_THREADS", -2) != NULL) {
- fprintf(stderr, "FAIL: device_id=-2 must return NULL\n");
- rc = 1;
- }
+ rc |= check(__kmp_resolve_host_env("OMP_NUM_THREADS"), -1, "8"); // host: _ALL
+ rc |=
+ check(__kmp_resolve_device_env("OMP_NUM_THREADS", 0), 0, "128"); // _DEV_0
+ rc |= check(__kmp_resolve_device_env("OMP_NUM_THREADS", 1), 1, "64"); // _DEV
+ rc |= check(__kmp_resolve_device_env("OMP_NUM_THREADS", 2), 2, "64"); // _DEV
return rc;
}
diff --git a/openmp/runtime/test/env/omp_num_threads_host_overrides_all.c b/openmp/runtime/test/env/omp_num_threads_host_overrides_all.c
index b6e90b5a368bf..f0f22c3c00252 100644
--- a/openmp/runtime/test/env/omp_num_threads_host_overrides_all.c
+++ b/openmp/runtime/test/env/omp_num_threads_host_overrides_all.c
@@ -1,5 +1,5 @@
-// RUN: %libomp-compile && env OMP_NUM_THREADS=4 OMP_NUM_THREADS_ALL=8
-// %libomp-run
+// RUN: %libomp-compile && \
+// RUN: env OMP_NUM_THREADS=4 OMP_NUM_THREADS_ALL=8 %libomp-run
//
// Host OMP_NUM_THREADS beats _ALL; legacy ICV and query API agree.
@@ -7,8 +7,8 @@
#include <stdio.h>
#include <string.h>
-extern const char *__kmpc_get_resolved_device_env(const char *name,
- int device_id);
+extern const char *__kmp_resolve_host_env(const char *name);
+extern const char *__kmp_resolve_device_env(const char *name, int device_id);
int main(void) {
int max = omp_get_max_threads();
@@ -16,14 +16,14 @@ int main(void) {
fprintf(stderr, "FAIL: omp_get_max_threads()=%d, expected 4\n", max);
return 1;
}
- const char *host = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", -1);
+ const char *host = __kmp_resolve_host_env("OMP_NUM_THREADS");
if (host == NULL || strcmp(host, "4") != 0) {
fprintf(stderr, "FAIL: host query expected '4' got '%s'\n",
host ? host : "(null)");
return 1;
}
// Sibling `_ALL` must still resolve for non-host devices.
- const char *dev0 = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", 0);
+ const char *dev0 = __kmp_resolve_device_env("OMP_NUM_THREADS", 0);
if (dev0 == NULL || strcmp(dev0, "8") != 0) {
fprintf(stderr, "FAIL: dev 0 query expected '8' got '%s'\n",
dev0 ? dev0 : "(null)");
diff --git a/openmp/runtime/test/env/omp_num_threads_host_vs_device.c b/openmp/runtime/test/env/omp_num_threads_host_vs_device.c
new file mode 100644
index 0000000000000..7c6f88e6698f9
--- /dev/null
+++ b/openmp/runtime/test/env/omp_num_threads_host_vs_device.c
@@ -0,0 +1,31 @@
+// RUN: %libomp-compile && \
+// RUN: env OMP_NUM_THREADS=4 OMP_NUM_THREADS_ALL=8 \
+// RUN: OMP_NUM_THREADS_DEV_0=128 %libomp-run
+//
+// Host and non-host devices resolve through separate functions.
+
+#include <omp.h>
+#include <stdio.h>
+#include <string.h>
+
+extern const char *__kmp_resolve_host_env(const char *name);
+extern const char *__kmp_resolve_device_env(const char *name, int device_id);
+
+int main(void) {
+ (void)omp_get_max_threads();
+
+ // Host uses the unsuffixed value, not _ALL.
+ const char *host = __kmp_resolve_host_env("OMP_NUM_THREADS");
+ if (host == NULL || strcmp(host, "4") != 0)
+ return 1;
+
+ // Device 0 uses _DEV_0; device 1 falls through to _ALL.
+ const char *dev0 = __kmp_resolve_device_env("OMP_NUM_THREADS", 0);
+ if (dev0 == NULL || strcmp(dev0, "128") != 0)
+ return 2;
+ const char *dev1 = __kmp_resolve_device_env("OMP_NUM_THREADS", 1);
+ if (dev1 == NULL || strcmp(dev1, "8") != 0)
+ return 3;
+
+ return 0;
+}
diff --git a/openmp/runtime/test/env/omp_num_threads_warnings_off.c b/openmp/runtime/test/env/omp_num_threads_warnings_off.c
index 6b8b631ec4fba..12a65aefb6f76 100644
--- a/openmp/runtime/test/env/omp_num_threads_warnings_off.c
+++ b/openmp/runtime/test/env/omp_num_threads_warnings_off.c
@@ -9,8 +9,7 @@
#include <stdio.h>
#include <string.h>
-extern const char *__kmpc_get_resolved_device_env(const char *name,
- int device_id);
+extern const char *__kmp_resolve_host_env(const char *name);
int main(void) {
int max = omp_get_max_threads();
@@ -18,7 +17,7 @@ int main(void) {
fprintf(stderr, "FAIL: omp_get_max_threads()=%d, expected 8\n", max);
return 1;
}
- const char *host = __kmpc_get_resolved_device_env("OMP_NUM_THREADS", -1);
+ const char *host = __kmp_resolve_host_env("OMP_NUM_THREADS");
if (host == NULL || strcmp(host, "8") != 0) {
fprintf(stderr, "FAIL: host query expected '8' got '%s'\n",
host ? host : "(null)");
More information about the Openmp-commits
mailing list