[llvm] [OFFLOAD] Add plugin with support for Intel oneAPI Level Zero (PR #158900)

Alexey Sachkov via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 16 06:23:40 PDT 2025


================
@@ -0,0 +1,371 @@
+//===--- Level Zero Target RTL Implementation -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Level Zero RTL Options support
+//
+//===----------------------------------------------------------------------===//
+
+#include "omptarget.h"
+
+#include "L0Defs.h"
+#include "L0Options.h"
+#include "L0Trace.h"
+
+namespace llvm::omp::target::plugin {
+
+/// Is the given RootID, SubID, CcsID specified in ONEAPI_DEVICE_SELECTOR
+bool L0OptionsTy::shouldAddDevice(int32_t RootID, int32_t SubID,
+                                  int32_t CCSID) const {
+  if (ExplicitRootDevices.empty())
+    return false;
+  for (const auto &RootDev : ExplicitRootDevices) {
+    const auto ErootID = std::get<1>(RootDev);
+    if (ErootID != -2 && RootID != ErootID)
+      continue;
+    const auto EsubID = std::get<2>(RootDev);
+    if (((EsubID != -2) || (SubID == -1)) && (EsubID != SubID))
+      continue;
+    const auto ECCSID = std::get<3>(RootDev);
+    if (((ECCSID != -2) || (CCSID == -1)) && (ECCSID != CCSID))
+      continue;
+    // Check if isDiscard
+    if (!std::get<0>(RootDev))
+      return false;
+    return true;
+  }
+  return false;
+}
+
+/// Read environment variables
+void L0OptionsTy::processEnvironmentVars() {
+  // Compilation options for IGC
+  UserCompilationOptions +=
+      std::string(" ") +
+      StringEnvar("LIBOMPTARGET_LEVEL_ZERO_COMPILATION_OPTIONS", "").get();
+
+  // Explicit Device mode if ONEAPI_DEVICE_SELECTOR is set
+  const StringEnvar DeviceSelectorVar("ONEAPI_DEVICE_SELECTOR", "");
+  if (DeviceSelectorVar.isPresent()) {
+    std::string EnvStr(std::move(DeviceSelectorVar.get()));
+    uint32_t numDiscard = 0;
+    std::transform(EnvStr.begin(), EnvStr.end(), EnvStr.begin(),
+                   [](unsigned char C) { return std::tolower(C); });
+
+    std::vector<std::string_view> Entries = tokenize(EnvStr, ";", true);
+    for (const auto &Term : Entries) {
+      bool isDiscard = false;
+      std::vector<std::string_view> Pair = tokenize(Term, ":", true);
+      if (Pair.empty()) {
+        FAILURE_MESSAGE(
+            "Incomplete selector! Pair and device must be specified.\n");
+      } else if (Pair.size() == 1) {
+        FAILURE_MESSAGE("Incomplete selector!  Try '%s:*'if all devices "
+                        "under the Pair was original intention.\n",
+                        Pair[0].data());
+      } else if (Pair.size() > 2) {
+        FAILURE_MESSAGE(
+            "Error parsing selector string \"%s\" Too many colons (:)\n",
+            Term.data());
+      }
+      if (!((Pair[0][0] == '*') ||
+            (!strncmp(Pair[0].data(), "level_zero", Pair[0].length())) ||
+            (!strncmp(Pair[0].data(), "!level_zero", Pair[0].length()))))
+        break;
+      isDiscard = Pair[0][0] == '!';
+      if (isDiscard)
+        numDiscard++;
+      else if (numDiscard > 0)
+        FAILURE_MESSAGE("All negative(discarding) filters must appear after "
+                        "all positive(accepting) filters!");
+
+      std::vector<std::string_view> Targets = tokenize(Pair[1], ",", true);
+      for (const auto &TargetStr : Targets) {
+        bool HasDeviceWildCard = false;
+        bool HasSubDeviceWildCard = false;
+        bool DeviceNum = false;
+        std::vector<std::string_view> DeviceSubTuple =
+            tokenize(TargetStr, ".", true);
+        int32_t RootD[3] = {-1, -1, -1};
+        if (DeviceSubTuple.empty()) {
+          FAILURE_MESSAGE(
+              "ONEAPI_DEVICE_SELECTOR parsing error. Device must be "
+              "specified.");
+        }
+
+        std::string_view TopDeviceStr = DeviceSubTuple[0];
+        static const std::array<std::string, 7> DeviceStr = {
+            "host", "cpu", "gpu", "acc", "fpga", "*"};
+        auto It =
+            find_if(DeviceStr.begin(), DeviceStr.end(),
+                    [&](auto DeviceStr) { return TopDeviceStr == DeviceStr; });
+        if (It != DeviceStr.end()) {
+          if (TopDeviceStr[0] == '*') {
+            HasDeviceWildCard = true;
+            RootD[0] = -2;
+          } else if (!strncmp(DeviceSubTuple[0].data(), "gpu", 3))
+            continue;
+        } else {
+          std::string TDS(TopDeviceStr);
+          if (!isDigits(TDS)) {
+            FAILURE_MESSAGE("error parsing device number: %s",
+                            DeviceSubTuple[0].data());
+          } else {
+            RootD[0] = std::stoi(TDS);
+            DeviceNum = true;
+          }
----------------
AlexeySachkov wrote:

Why do we convert a `string_view` back to `string` in order to check that it only contains digits?

`StringRef` is constructible from `string_view` and has method `getAsInteger` - I suggest we use it instead of custom error checking.

https://github.com/llvm/llvm-project/pull/158900


More information about the llvm-commits mailing list