[llvm-branch-commits] [openmp] [libomp] Parse OMP_DEFAULT_DEVICE with new device trait parser (PR #176166)

Robert Imschweiler via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jun 18 05:14:57 PDT 2026


https://github.com/ro-i updated https://github.com/llvm/llvm-project/pull/176166

>From 60416cf8f54ee83b4d7b369043970d6e8ec5aa74 Mon Sep 17 00:00:00 2001
From: Robert Imschweiler <robert.imschweiler at amd.com>
Date: Thu, 15 Jan 2026 06:49:10 -0600
Subject: [PATCH] [libomp] Parse OMP_DEFAULT_DEVICE with new device trait
 parser

... but do not yet expose the new functionalities to the user. This is a
backward compatible update that is going to be followed by the step to
the OpenMP 6.0 semantics as defined in 4.3.8.
---
 openmp/runtime/src/i18n/en_US.txt             |   3 +
 openmp/runtime/src/kmp_settings.cpp           |   5 +-
 openmp/runtime/src/kmp_traits.cpp             |  24 ++++
 openmp/runtime/src/kmp_traits.h               |   8 ++
 .../unittests/Traits/TestOMPTraitParser.cpp   | 105 ++++++++++++++++++
 5 files changed, 143 insertions(+), 2 deletions(-)

diff --git a/openmp/runtime/src/i18n/en_US.txt b/openmp/runtime/src/i18n/en_US.txt
index f45b2e70d1547..00ab391944292 100644
--- a/openmp/runtime/src/i18n/en_US.txt
+++ b/openmp/runtime/src/i18n/en_US.txt
@@ -365,6 +365,9 @@ TopologyHybridCoreEff        "%1$s:   %2$d with core efficiency %3$d."
 TraitParserInvalidUID        "trait parser while parsing %1$s: invalid uid (%2$s)"
 TraitParserMaxRecursion      "trait parser while parsing %1$s: max recursion depth (%2$d) exceeded"
 TraitParserFailed            "trait parser while parsing %1$s: failed to parse trait specification (%2$s)"
+TraitParserValueTooSmall     "trait parser while parsing %1$s: value %2$d below limit (%3$d)"
+TraitParserValueTooLarge     "trait parser while parsing %1$s: value %2$d above limit (%3$d)"
+TraitParserLegacyFailed      "legacy device number parser while parsing %1$s: failed to parse device number (%2$s)"
 
 # --- OpenMP errors detected at runtime ---
 #
diff --git a/openmp/runtime/src/kmp_settings.cpp b/openmp/runtime/src/kmp_settings.cpp
index 66ef6f8097dce..34ccc0f096f01 100644
--- a/openmp/runtime/src/kmp_settings.cpp
+++ b/openmp/runtime/src/kmp_settings.cpp
@@ -23,6 +23,7 @@
 #include "kmp_lock.h"
 #include "kmp_settings.h"
 #include "kmp_str.h"
+#include "kmp_traits.h"
 #include "kmp_wrapper_getpid.h"
 #include <ctype.h> // toupper()
 #if OMPD_SUPPORT
@@ -1415,8 +1416,8 @@ static void __kmp_stg_print_max_active_levels(kmp_str_buf_t *buffer,
 // OpenMP 4.0: OMP_DEFAULT_DEVICE
 static void __kmp_stg_parse_default_device(char const *name, char const *value,
                                            void *data) {
-  __kmp_stg_parse_int(name, value, 0, KMP_MAX_DEFAULT_DEVICE_LIMIT,
-                      &__kmp_default_device);
+  __kmp_default_device = kmp_trait_context::parse_single_device(
+      value, /*device_num_min=*/0, KMP_MAX_DEFAULT_DEVICE_LIMIT, name);
 } // __kmp_stg_parse_default_device
 
 static void __kmp_stg_print_default_device(kmp_str_buf_t *buffer,
diff --git a/openmp/runtime/src/kmp_traits.cpp b/openmp/runtime/src/kmp_traits.cpp
index ad307833c6aac..f60c9c7fc81c4 100644
--- a/openmp/runtime/src/kmp_traits.cpp
+++ b/openmp/runtime/src/kmp_traits.cpp
@@ -292,3 +292,27 @@ kmp_trait_context *kmp_trait_context::parse_from_spec(kmp_str_ref spec,
     KMP_FATAL(TraitParserFailed, dbg_name, spec.copy());
   return context;
 }
+
+int kmp_trait_context::parse_single_device(kmp_str_ref spec, int device_num_min,
+                                           int device_num_max,
+                                           const char *dbg_name) {
+  int device_num;
+  kmp_str_ref orig_spec = spec;
+
+  spec.skip_space();
+  if (!spec.consume_integer(device_num, /*allow_zero=*/true,
+                            /*allow_negative=*/true))
+    KMP_FATAL(TraitParserLegacyFailed, dbg_name, orig_spec.copy());
+  spec.skip_space();
+  if (!spec.empty())
+    KMP_FATAL(TraitParserLegacyFailed, dbg_name, orig_spec.copy());
+
+  if (device_num < device_num_min) {
+    KMP_WARNING(TraitParserValueTooSmall, dbg_name, device_num, device_num_min);
+    device_num = device_num_min;
+  } else if (device_num > device_num_max) {
+    KMP_WARNING(TraitParserValueTooLarge, dbg_name, device_num, device_num_max);
+    device_num = device_num_max;
+  }
+  return device_num;
+}
diff --git a/openmp/runtime/src/kmp_traits.h b/openmp/runtime/src/kmp_traits.h
index 052017a66225f..e3e881ae09797 100644
--- a/openmp/runtime/src/kmp_traits.h
+++ b/openmp/runtime/src/kmp_traits.h
@@ -361,6 +361,14 @@ class kmp_trait_context final {
   static kmp_trait_context *parse_from_spec(kmp_str_ref spec,
                                             const char *dbg_name = nullptr);
 
+  // Parse only a single device number from the spec.
+  // This is useful for backward compatibility with legacy code.
+  // If dbg_name is provided, it will be used in error messages to identify the
+  // source of the device number.
+  static int parse_single_device(kmp_str_ref spec, int device_num_min,
+                                 int device_num_max,
+                                 const char *dbg_name = nullptr);
+
   void add_clause(kmp_trait_clause *clause) {
     assert(clause);
     clauses.push_back(clause);
diff --git a/openmp/runtime/unittests/Traits/TestOMPTraitParser.cpp b/openmp/runtime/unittests/Traits/TestOMPTraitParser.cpp
index 08f19ad72e322..536ce3caf3f6c 100644
--- a/openmp/runtime/unittests/Traits/TestOMPTraitParser.cpp
+++ b/openmp/runtime/unittests/Traits/TestOMPTraitParser.cpp
@@ -978,4 +978,109 @@ TEST_F(ParserTest, DoubleComma) {
                "failed to parse trait specification \\(,,uid\\(b\\)\\)");
 }
 
+//===----------------------------------------------------------------------===//
+// parse_single_device Tests
+//===----------------------------------------------------------------------===//
+
+TEST(ParseSingleDeviceTest, ValidSingleDigit) {
+  int result = kmp_trait_context::parse_single_device(kmp_str_ref("5"), 0, 10);
+  EXPECT_EQ(result, 5);
+}
+
+TEST(ParseSingleDeviceTest, ValidMultiDigit) {
+  int result =
+      kmp_trait_context::parse_single_device(kmp_str_ref("123"), 0, 200);
+  EXPECT_EQ(result, 123);
+}
+
+TEST(ParseSingleDeviceTest, Zero) {
+  int result = kmp_trait_context::parse_single_device(kmp_str_ref("0"), 0, 10);
+  EXPECT_EQ(result, 0);
+}
+
+TEST(ParseSingleDeviceTest, AtMax) {
+  int result = kmp_trait_context::parse_single_device(kmp_str_ref("10"), 0, 10);
+  EXPECT_EQ(result, 10);
+}
+
+TEST(ParseSingleDeviceTest, AtMin) {
+  int result = kmp_trait_context::parse_single_device(kmp_str_ref("2"), 2, 10);
+  EXPECT_EQ(result, 2);
+}
+
+TEST(ParseSingleDeviceTest, AboveMaxClampsToMax) {
+  // Values above the maximum are warned about and clamped to the maximum.
+  int result = kmp_trait_context::parse_single_device(kmp_str_ref("11"), 0, 10);
+  EXPECT_EQ(result, 10);
+}
+
+TEST(ParseSingleDeviceTest, BelowMinClampsToMin) {
+  // Values below the minimum are warned about and clamped to the minimum.
+  int result = kmp_trait_context::parse_single_device(kmp_str_ref("2"), 5, 10);
+  EXPECT_EQ(result, 5);
+}
+
+TEST(ParseSingleDeviceTest, NegativeValue) {
+  // Negative device numbers are allowed within range.
+  int result =
+      kmp_trait_context::parse_single_device(kmp_str_ref("-1"), -5, 10);
+  EXPECT_EQ(result, -1);
+}
+
+TEST(ParseSingleDeviceTest, NegativeBelowMinClampsToMin) {
+  // Negative values below the minimum are clamped to the minimum.
+  int result = kmp_trait_context::parse_single_device(kmp_str_ref("-7"), 0, 10);
+  EXPECT_EQ(result, 0);
+}
+
+TEST(ParseSingleDeviceTest, NonInteger) {
+  ASSERT_DEATH(kmp_trait_context::parse_single_device(kmp_str_ref("abc"), 0, 10,
+                                                      "non_integer"),
+               "OMP: Error #[0-9]+: legacy device number parser while parsing "
+               "non_integer: failed to parse device number \\(abc\\)");
+}
+
+TEST(ParseSingleDeviceTest, EmptyString) {
+  ASSERT_DEATH(
+      kmp_trait_context::parse_single_device(kmp_str_ref(""), 0, 10, "empty"),
+      "OMP: Error #[0-9]+: legacy device number parser while parsing empty: "
+      "failed to parse device number \\(\\)");
+}
+
+TEST(ParseSingleDeviceTest, LeadingSpaces) {
+  // consume_integer skips leading spaces
+  int result =
+      kmp_trait_context::parse_single_device(kmp_str_ref("  7"), 0, 10);
+  EXPECT_EQ(result, 7);
+}
+
+TEST(ParseSingleDeviceTest, LargeNumber) {
+  int result =
+      kmp_trait_context::parse_single_device(kmp_str_ref("999999"), 0, 1000000);
+  EXPECT_EQ(result, 999999);
+}
+
+TEST(ParseSingleDeviceTest, TrailingGarbage) {
+  // Trailing non-integer characters must not be silently ignored.
+  ASSERT_DEATH(kmp_trait_context::parse_single_device(kmp_str_ref("10abc"), 0,
+                                                      100, "trailing_garbage"),
+               "OMP: Error #[0-9]+: legacy device number parser while parsing "
+               "trailing_garbage: failed to parse device number \\(10abc\\)");
+}
+
+TEST(ParseSingleDeviceTest, TrailingList) {
+  // Only a single device is accepted; a comma-separated list must fail.
+  ASSERT_DEATH(kmp_trait_context::parse_single_device(kmp_str_ref("10,11"), 0,
+                                                      100, "trailing_list"),
+               "OMP: Error #[0-9]+: legacy device number parser while parsing "
+               "trailing_list: failed to parse device number \\(10,11\\)");
+}
+
+TEST(ParseSingleDeviceTest, TrailingSpaces) {
+  // Trailing whitespace is allowed.
+  int result =
+      kmp_trait_context::parse_single_device(kmp_str_ref("7  "), 0, 10);
+  EXPECT_EQ(result, 7);
+}
+
 } // namespace



More information about the llvm-branch-commits mailing list