[llvm] [offload] Add Windows offload support (PR #187006)

Weronika Lewandowska via llvm-commits llvm-commits at lists.llvm.org
Thu May 7 02:05:36 PDT 2026


https://github.com/wlemkows updated https://github.com/llvm/llvm-project/pull/187006

>From 443743aa8afa6c1c5cf98f91ccf13bae853ef65f Mon Sep 17 00:00:00 2001
From: Weronika Lewandowska <weronika.lewandowska at intel.com>
Date: Thu, 15 Jan 2026 14:32:02 +0100
Subject: [PATCH] [Offload] Add Windows offload support

---
 llvm/runtimes/CMakeLists.txt                  |  1 +
 offload/CMakeLists.txt                        | 70 +++++++++++++++----
 offload/include/omptarget.h                   |  2 +-
 offload/liboffload/CMakeLists.txt             | 18 ++++-
 offload/liboffload/src/OffloadImpl.cpp        |  2 +-
 offload/plugins-nextgen/CMakeLists.txt        | 10 +++
 .../common/include/ErrorReporting.h           |  4 ++
 .../common/include/GlobalHandler.h            |  3 +-
 .../common/src/GlobalHandler.cpp              | 13 ++++
 .../common/src/RecordReplay.cpp               |  6 +-
 .../plugins-nextgen/common/src/Utils/ELF.cpp  |  9 ++-
 .../level_zero/include/L0Device.h             |  5 +-
 offload/tools/CMakeLists.txt                  |  6 +-
 13 files changed, 126 insertions(+), 23 deletions(-)

diff --git a/llvm/runtimes/CMakeLists.txt b/llvm/runtimes/CMakeLists.txt
index ed96a593329ab..1baaa593c9087 100644
--- a/llvm/runtimes/CMakeLists.txt
+++ b/llvm/runtimes/CMakeLists.txt
@@ -305,6 +305,7 @@ function(runtime_default_target)
                                                 CUDA CMAKE_CUDA # For runtimes that may look for the CUDA compiler and/or SDK (libc, offload, flang-rt)
                                                 FFI # offload uses libffi
                                                 FLANG_RUNTIME # Shared between Flang and Flang-RT
+                                                LIBOMPTARGET # offload plugin selection
                                                 ${ARG_PREFIXES}
                            EXTRA_TARGETS ${extra_targets}
                                          ${test_targets}
diff --git a/offload/CMakeLists.txt b/offload/CMakeLists.txt
index 472fee8e64156..5a8f735d545e4 100644
--- a/offload/CMakeLists.txt
+++ b/offload/CMakeLists.txt
@@ -9,8 +9,8 @@ set(OFFLOAD_MIN_CUDA_VERSION 11.8.0)
 set(OFFLOAD_MIN_CUDA_VERSION_CODE 11080)
 
 # Check that the library can actually be built.
-if(APPLE OR WIN32 OR WASM)
-  message(WARNING "libomptarget cannot be built on Windows and MacOS X!")
+if(APPLE OR WASM)
+  message(WARNING "libomptarget cannot be built on MacOS X and WASM!")
   return()
 elseif("${LLVM_DEFAULT_TARGET_TRIPLE}" MATCHES "^(amdgcn|nvptx|spirv)" OR
        "${CMAKE_CXX_COMPILER_TARGET}" MATCHES "^(amdgcn|nvptx|spirv)")
@@ -43,6 +43,11 @@ endif()
 
 set(LLVM_COMMON_CMAKE_UTILS ${CMAKE_CURRENT_SOURCE_DIR}/../cmake)
 
+if(WIN32)
+  # These are needed for RtlGetLastNtStatus and delay-load helpers
+  add_link_options(/DEFAULTLIB:ntdll.lib /DEFAULTLIB:delayimp.lib)
+endif()
+
 option(OFFLOAD_INCLUDE_TESTS "Generate and build offload tests." ${LLVM_INCLUDE_TESTS})
 
 # Add path for custom modules
@@ -122,17 +127,37 @@ if(DEFINED LIBOMPTARGET_BUILD_CUDA_PLUGIN OR
   message(WARNING "Option removed, use 'LIBOMPTARGET_PLUGINS_TO_BUILD' instead")
 endif()
 
-set(LIBOMPTARGET_ALL_PLUGIN_TARGETS amdgpu cuda)
-set(LIBOMPTARGET_PLUGINS_TO_BUILD "all" CACHE STRING
-    "Semicolon-separated list of plugins to use: cuda, amdgpu, level_zero, or \"all\".")
+if(WIN32)
+  set(LIBOMPTARGET_ALL_PLUGIN_TARGETS "")
+
+  set(LIBOMPTARGET_PLUGINS_TO_BUILD "" CACHE STRING
+      "No plugins are built on Windows by default. Use LIBOMPTARGET_PLUGINS_TO_BUILD option to enable plugins.")
+  # Validate that only supported plugins are requested on Windows.
+  foreach(plugin IN LISTS LIBOMPTARGET_PLUGINS_TO_BUILD)
+    if(NOT plugin STREQUAL "level_zero")
+      message(FATAL_ERROR "Plugin '${plugin}' is not supported on Windows.")
+    endif()
+  endforeach()
+else()
+  set(LIBOMPTARGET_ALL_PLUGIN_TARGETS amdgpu cuda)
+  set(LIBOMPTARGET_PLUGINS_TO_BUILD "all" CACHE STRING
+      "Semicolon-separated list of plugins to use: cuda, amdgpu or \"all\".")
 
-if(LIBOMPTARGET_PLUGINS_TO_BUILD STREQUAL "all")
-  set(LIBOMPTARGET_PLUGINS_TO_BUILD ${LIBOMPTARGET_ALL_PLUGIN_TARGETS})
+  if(LIBOMPTARGET_PLUGINS_TO_BUILD STREQUAL "all")
+    set(LIBOMPTARGET_PLUGINS_TO_BUILD ${LIBOMPTARGET_ALL_PLUGIN_TARGETS})
+  endif()
 endif()
 
 list(APPEND LIBOMPTARGET_PLUGINS_TO_BUILD "host")
 list(REMOVE_DUPLICATES LIBOMPTARGET_PLUGINS_TO_BUILD)
 
+if(WIN32)
+  if("host" IN_LIST LIBOMPTARGET_PLUGINS_TO_BUILD)
+    message(STATUS "Not building host plugin: it is not supported on Windows")
+    list(REMOVE_ITEM LIBOMPTARGET_PLUGINS_TO_BUILD "host")
+  endif()
+endif()
+
 if(NOT (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(ppc64le)|(aarch64)$"
         AND CMAKE_SYSTEM_NAME MATCHES "Linux"))
   if("amdgpu" IN_LIST LIBOMPTARGET_PLUGINS_TO_BUILD)
@@ -165,7 +190,7 @@ foreach(plugin IN LISTS LIBOMPTARGET_PLUGINS_TO_BUILD)
   set(LIBOMPTARGET_ENUM_PLUGIN_TARGETS
       "${LIBOMPTARGET_ENUM_PLUGIN_TARGETS}PLUGIN_TARGET(${plugin})\n")
 endforeach()
-string(STRIP ${LIBOMPTARGET_ENUM_PLUGIN_TARGETS} LIBOMPTARGET_ENUM_PLUGIN_TARGETS)
+string(STRIP "${LIBOMPTARGET_ENUM_PLUGIN_TARGETS}" LIBOMPTARGET_ENUM_PLUGIN_TARGETS)
 configure_file(
   ${CMAKE_CURRENT_SOURCE_DIR}/include/Shared/Targets.def.in
   ${CMAKE_CURRENT_BINARY_DIR}/include/Shared/Targets.def
@@ -213,10 +238,20 @@ if(LIBOMPTARGET_ENABLE_DEBUG)
 endif()
 
 # No exceptions and no RTTI, except if requested.
-set(offload_compile_flags -fno-exceptions)
+if(MSVC)
+  set(offload_compile_flags /EHs-c-)
+else()
+  set(offload_compile_flags -fno-exceptions)
+endif()
+
 if(NOT LLVM_ENABLE_RTTI)
-  set(offload_compile_flags ${offload_compile_flags} -fno-rtti)
+  if(MSVC)
+    list(APPEND offload_compile_flags /GR-)
+  else()
+    list(APPEND offload_compile_flags -fno-rtti)
+  endif()
 endif()
+
 if(OFFLOAD_HAVE_WERROR_CTOR)
   list(APPEND offload_compile_flags -Werror=global-constructors)
 endif()
@@ -296,8 +331,19 @@ add_subdirectory(plugins-nextgen)
 add_subdirectory(tools)
 add_subdirectory(docs)
 
-# Build target agnostic offloading library.
-add_subdirectory(libomptarget)
+# Allow disabling libomptarget (e.g., on Windows) while still building
+# liboffload and plugins.
+if (WIN32)
+  set(LIBOMPTARGET_ENABLE_LIBOMPTARGET_DEFAULT OFF)
+else()
+  set(LIBOMPTARGET_ENABLE_LIBOMPTARGET_DEFAULT ON)
+endif()
+option(LIBOMPTARGET_ENABLE_LIBOMPTARGET
+       "Build the libomptarget runtime library"
+       ${LIBOMPTARGET_ENABLE_LIBOMPTARGET_DEFAULT})
+if(LIBOMPTARGET_PLUGINS_TO_BUILD AND LIBOMPTARGET_ENABLE_LIBOMPTARGET)
+  add_subdirectory(libomptarget)
+endif()
 
 add_subdirectory(liboffload)
 
diff --git a/offload/include/omptarget.h b/offload/include/omptarget.h
index e5d9852ad48a6..db9590844b2fd 100644
--- a/offload/include/omptarget.h
+++ b/offload/include/omptarget.h
@@ -46,7 +46,7 @@ enum __tgt_target_return_t : int {
 };
 
 /// Data attributes for each data reference used in an OpenMP target region.
-enum tgt_map_type {
+enum tgt_map_type : uint64_t {
   // No flags
   OMP_TGT_MAPTYPE_NONE = 0x000,
   // copy data from host to device
diff --git a/offload/liboffload/CMakeLists.txt b/offload/liboffload/CMakeLists.txt
index 62480dad1cac8..d70d1da418221 100644
--- a/offload/liboffload/CMakeLists.txt
+++ b/offload/liboffload/CMakeLists.txt
@@ -7,13 +7,17 @@ add_llvm_library(
 
   LINK_COMPONENTS
   FrontendOpenMP
+  Passes
   Support
 
   DEPENDS
   OffloadAPI
   PluginErrcodes
+  PluginCommon
   )
 
+target_link_libraries(LLVMOffload PRIVATE PluginCommon)
+
 foreach(plugin IN LISTS LIBOMPTARGET_PLUGINS_TO_BUILD)
     target_link_libraries(LLVMOffload PRIVATE omptarget.rtl.${plugin})
 endforeach()
@@ -27,6 +31,7 @@ target_include_directories(LLVMOffload PUBLIC
                             ${CMAKE_CURRENT_BINARY_DIR}/../include
                             ${CMAKE_CURRENT_SOURCE_DIR}/include
                             ${CMAKE_CURRENT_SOURCE_DIR}/../include
+                            PRIVATE
                             ${CMAKE_CURRENT_SOURCE_DIR}/../plugins-nextgen/common/include
                             ${CMAKE_CURRENT_BINARY_DIR}/../plugins-nextgen/common/include
                             )
@@ -43,7 +48,18 @@ set_target_properties(LLVMOffload PROPERTIES
                       POSITION_INDEPENDENT_CODE ON
                       INSTALL_RPATH "$ORIGIN"
                       BUILD_RPATH "$ORIGIN:${CMAKE_CURRENT_BINARY_DIR}/..")
-install(TARGETS LLVMOffload LIBRARY COMPONENT LLVMOffload DESTINATION "${OFFLOAD_INSTALL_LIBDIR}")
+install(TARGETS LLVMOffload
+        COMPONENT LLVMOffload
+        RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+        LIBRARY DESTINATION "${OFFLOAD_INSTALL_LIBDIR}"
+        ARCHIVE DESTINATION "${OFFLOAD_INSTALL_LIBDIR}")
+
+if(MSVC)
+  install(FILES "$<TARGET_PDB_FILE:LLVMOffload>"
+          CONFIGURATIONS Debug RelWithDebInfo
+          DESTINATION "${CMAKE_INSTALL_BINDIR}"
+          OPTIONAL)
+endif()
 
 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/API/OffloadAPI.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/offload)
 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/API/OffloadPrint.hpp DESTINATION ${CMAKE_INSTALL_PREFIX}/include/offload)
diff --git a/offload/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp
index 71712c2bc6b45..4f8574dbbad17 100644
--- a/offload/liboffload/src/OffloadImpl.cpp
+++ b/offload/liboffload/src/OffloadImpl.cpp
@@ -258,7 +258,7 @@ template <typename HandleT> Error olDestroy(HandleT Handle) {
   return Error::success();
 }
 
-constexpr ol_platform_backend_t pluginNameToBackend(StringRef Name) {
+ol_platform_backend_t pluginNameToBackend(StringRef Name) {
   if (Name == "amdgpu") {
     return OL_PLATFORM_BACKEND_AMDGPU;
   } else if (Name == "cuda") {
diff --git a/offload/plugins-nextgen/CMakeLists.txt b/offload/plugins-nextgen/CMakeLists.txt
index 5165ed173f19f..33827b30283a0 100644
--- a/offload/plugins-nextgen/CMakeLists.txt
+++ b/offload/plugins-nextgen/CMakeLists.txt
@@ -2,6 +2,16 @@
 set(common_dir ${CMAKE_CURRENT_SOURCE_DIR}/common)
 set(common_bin_dir ${CMAKE_CURRENT_BINARY_DIR}/common)
 add_subdirectory(common)
+
+# Only build specific plugins if any are requested
+if(NOT LIBOMPTARGET_PLUGINS_TO_BUILD)
+  message(STATUS "Skipping plugin implementations (no plugins enabled)")
+  # Set empty lists for parent scope
+  set(LIBOMPTARGET_SYSTEM_TARGETS "" PARENT_SCOPE)
+  set(LIBOMPTARGET_TESTED_PLUGINS "" PARENT_SCOPE)
+  return()
+endif()
+
 function(add_target_library target_name lib_name)
   add_llvm_library(${target_name} STATIC
     LINK_COMPONENTS
diff --git a/offload/plugins-nextgen/common/include/ErrorReporting.h b/offload/plugins-nextgen/common/include/ErrorReporting.h
index ff7d9b4f39d2b..e6f65549bc354 100644
--- a/offload/plugins-nextgen/common/include/ErrorReporting.h
+++ b/offload/plugins-nextgen/common/include/ErrorReporting.h
@@ -72,9 +72,11 @@ class ErrorReporter {
     llvm_unreachable("Unknown target alloc kind");
   }
 
+#ifdef __clang__
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wgcc-compat"
 #pragma clang diagnostic ignored "-Wformat-security"
+#endif
   /// Print \p Format, instantiated with \p Args to stderr.
   /// TODO: Allow redirection into a file stream.
   template <typename... ArgsTy>
@@ -109,7 +111,9 @@ class ErrorReporter {
     print(BoldRed, Format, Args...);
     print("\n");
   }
+#ifdef __clang__
 #pragma clang diagnostic pop
+#endif
 
   static void reportError(const char *Str) { reportError("%s", Str); }
   static void print(const char *Str) { print("%s", Str); }
diff --git a/offload/plugins-nextgen/common/include/GlobalHandler.h b/offload/plugins-nextgen/common/include/GlobalHandler.h
index fc8d6fe384754..5c1786afb9e89 100644
--- a/offload/plugins-nextgen/common/include/GlobalHandler.h
+++ b/offload/plugins-nextgen/common/include/GlobalHandler.h
@@ -18,6 +18,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/ProfileData/InstrProf.h"
+#include "llvm/Support/Compiler.h"
 
 #include "Shared/Debug.h"
 #include "Shared/Utils.h"
@@ -72,7 +73,7 @@ struct __llvm_profile_gpu_sections {
 };
 
 extern "C" {
-extern int __attribute__((weak)) __llvm_write_custom_profile(
+extern int LLVM_ATTRIBUTE_WEAK __llvm_write_custom_profile(
     const char *Target, const __llvm_profile_data *DataBegin,
     const __llvm_profile_data *DataEnd, const char *CountersBegin,
     const char *CountersEnd, const char *NamesBegin, const char *NamesEnd,
diff --git a/offload/plugins-nextgen/common/src/GlobalHandler.cpp b/offload/plugins-nextgen/common/src/GlobalHandler.cpp
index 9216834b1e15e..796ac2b33829a 100644
--- a/offload/plugins-nextgen/common/src/GlobalHandler.cpp
+++ b/offload/plugins-nextgen/common/src/GlobalHandler.cpp
@@ -30,6 +30,19 @@ using namespace plugin;
 using namespace error;
 using namespace llvm::offload::debug;
 
+// Windows/MSVC doesn't support weak symbols properly, so provide a stub
+// implementation that will be used if compiler-rt is not linked.
+#ifdef _WIN32
+extern "C" int __llvm_write_custom_profile(
+    const char *Target, const __llvm_profile_data *DataBegin,
+    const __llvm_profile_data *DataEnd, const char *CountersBegin,
+    const char *CountersEnd, const char *NamesBegin, const char *NamesEnd,
+    const uint64_t *VersionOverride) {
+  // Return error code indicating profiling is not available
+  return -1;
+}
+#endif
+
 Expected<std::unique_ptr<ObjectFile>>
 GenericGlobalHandlerTy::getELFObjectFile(DeviceImageTy &Image) {
   assert(utils::elf::isELF(Image.getMemoryBuffer().getBuffer()) &&
diff --git a/offload/plugins-nextgen/common/src/RecordReplay.cpp b/offload/plugins-nextgen/common/src/RecordReplay.cpp
index 8c4b012fdc9e2..a3ad160c87e59 100644
--- a/offload/plugins-nextgen/common/src/RecordReplay.cpp
+++ b/offload/plugins-nextgen/common/src/RecordReplay.cpp
@@ -69,9 +69,9 @@ Error RecordReplayTy::init(uint64_t MemSize, void *VAddr) {
 
   INFO(OMP_INFOTYPE_PLUGIN_KERNEL, Device.getDeviceId(),
        "%s initialized with starting address %p, "
-       "memory size %lu bytes, and output directory in %s\n",
+       "memory size %" PRIu64 " bytes, and output directory in %s\n",
        Status == StatusTy::Recording ? "Record" : "Replay", StartAddr,
-       TotalSize, OutputDirectory.c_str());
+       TotalSize, OutputDirectory.string().c_str());
 
   return Plugin::success();
 }
@@ -308,7 +308,7 @@ NativeRecordReplayTy::getFilenameImpl(const InstanceTy &Instance,
   Filepath /= std::to_string(Instance.KernelHash) + "_" +
               std::to_string(Instance.LaunchConfigHash);
   Filepath.replace_extension(getExtension(FileType).data());
-  SmallString<128> Filename(Filepath.c_str());
+  SmallString<128> Filename(Filepath.string());
   return Filename;
 }
 
diff --git a/offload/plugins-nextgen/common/src/Utils/ELF.cpp b/offload/plugins-nextgen/common/src/Utils/ELF.cpp
index 9762a5d738db8..6ddf8f1a3d270 100644
--- a/offload/plugins-nextgen/common/src/Utils/ELF.cpp
+++ b/offload/plugins-nextgen/common/src/Utils/ELF.cpp
@@ -50,7 +50,14 @@ uint16_t utils::elf::getTargetMachine() {
 #elif defined(__loongarch__)
   return EM_LOONGARCH;
 #else
-#warning "Unknown ELF compilation target architecture"
+// #warning directive is available starting in C23 and C++23.
+#define UNKNOWN_ARCH_MSG "Unknown ELF compilation target architecture"
+#ifdef _MSC_VER
+#pragma message(UNKNOWN_ARCH_MSG)
+#else
+#warning UNKNOWN_ARCH_MSG
+#endif
+#undef UNKNOWN_ARCH_MSG
   return EM_NONE;
 #endif
 }
diff --git a/offload/plugins-nextgen/level_zero/include/L0Device.h b/offload/plugins-nextgen/level_zero/include/L0Device.h
index 1ca62145f536e..ecb9ea026ac6b 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Device.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Device.h
@@ -22,6 +22,7 @@
 #include "L0Program.h"
 #include "PluginInterface.h"
 #include "TLS.h"
+#include <limits>
 
 namespace llvm::omp::target::plugin {
 
@@ -207,8 +208,8 @@ class L0DeviceTy final : public GenericDeviceTy {
   std::string zeId;
 
   /// Command queue group ordinals for each device.
-  static constexpr uint32_t MaxOrdinal =
-      std::numeric_limits<decltype(MaxOrdinal)>::max();
+  static constexpr uint32_t MaxOrdinal = std::numeric_limits<uint32_t>::max();
+
   std::pair<uint32_t, uint32_t> ComputeOrdinal{MaxOrdinal, 0};
   /// Command queue group ordinals for copying.
   std::pair<uint32_t, uint32_t> CopyOrdinal{MaxOrdinal, 0};
diff --git a/offload/tools/CMakeLists.txt b/offload/tools/CMakeLists.txt
index 7010ba93a4322..ec539f0b57587 100644
--- a/offload/tools/CMakeLists.txt
+++ b/offload/tools/CMakeLists.txt
@@ -11,4 +11,8 @@ macro(add_offload_tool_symlink name)
 endmacro()
 
 add_subdirectory(deviceinfo)
-add_subdirectory(kernelreplay)
+
+# kernelreplay requires libomptarget which is not available on Windows
+if(NOT WIN32)
+  add_subdirectory(kernelreplay)
+endif()



More information about the llvm-commits mailing list