[clang] [Driver][SYCL][Windows] Add SYCL windows runtime library linking support for SYCL JIT compilations (PR #194744)
Srividya Sundaram via cfe-commits
cfe-commits at lists.llvm.org
Tue May 5 15:45:53 PDT 2026
https://github.com/srividya-sundaram updated https://github.com/llvm/llvm-project/pull/194744
>From c83935594b5bcdd2a7744fcd0d4527b7e8734e4f Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Tue, 28 Apr 2026 15:01:38 -0700
Subject: [PATCH 1/5] [Driver][SYCL][Windows] Add runtime library linking with
CRT validation, phase1
---
.../clang/Basic/DiagnosticDriverKinds.td | 5 +
clang/include/clang/Options/Options.td | 2 +
clang/lib/Driver/ToolChains/MSVC.cpp | 108 +++++++++++++++++-
3 files changed, 114 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 469045948a47c..690228317e2be 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -93,6 +93,11 @@ def err_drv_hipspv_no_hip_path : Error<
"'--hip-path' must be specified when offloading to SPIR-V unless '-nogpuinc' "
"is given">;
+// SYCL Windows-specific diagnostics
+def err_drv_sycl_requires_dynamic_crt : Error<
+ "SYCL requires dynamic C++ runtime (/MD or /MDd); "
+ "static C++ runtime (/MT or /MTd) is not supported">;
+
def err_drv_no_spv_tools : Error<"cannot find SPIR-V Tools binary '%0', which "
"is required for SPIR-V compilation. "
"It can be obtained from your system package "
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index c16c41ad4057d..9bc8c981cb014 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -7566,6 +7566,8 @@ def fsycl : Flag<["-"], "fsycl">,
HelpText<"Enable SYCL C++ extensions">;
def fno_sycl : Flag<["-"], "fno-sycl">,
HelpText<"Disable SYCL C++ extensions">;
+def nolibsycl : Flag<["-"], "nolibsycl">,
+ HelpText<"Do not link the SYCL runtime library">;
def fsycl_device_only : Flag<["-"], "fsycl-device-only">,
Alias<offload_device_only>, HelpText<"Compile SYCL code for device only">;
def fsycl_host_only : Flag<["-"], "fsycl-host-only">,
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index f57440855fce4..2e052f6e282b1 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -85,9 +85,109 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(Args.MakeArgString("--dependent-lib=amath"));
}
+ // ==========================================================================
+ // SYCL: Auto-add /MD if no C++ runtime specified
+ // SYCL requires dynamic C++ runtime because STL objects cross DLL boundaries
+ // This must happen BEFORE other CRT handling logic
+ // ==========================================================================
+ if (Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false) &&
+ !Args.hasArg(options::OPT_nolibsycl) &&
+ !Args.hasArg(options::OPT_nostdlib) &&
+ !Args.hasArg(options::OPT_nostartfiles)) {
+
+ bool HasExplicitCRT = false;
+
+ // Check if user already specified a CRT flag
+ if (Args.hasArg(options::OPT__SLASH_MT) ||
+ Args.hasArg(options::OPT__SLASH_MTd) ||
+ Args.hasArg(options::OPT__SLASH_MD) ||
+ Args.hasArg(options::OPT__SLASH_MDd) ||
+ Args.hasArg(options::OPT_fms_runtime_lib_EQ))
+ HasExplicitCRT = true;
+
+ // Auto-add /MD if no CRT specified (makes dynamic CRT the default for SYCL)
+ if (!HasExplicitCRT) {
+ CmdArgs.push_back("/MD");
+ }
+ }
+
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles) &&
!C.getDriver().IsCLMode() && !C.getDriver().IsFlangMode()) {
- CmdArgs.push_back("-defaultlib:libcmt");
+
+ // ========================================================================
+ // SYCL: Validate CRT compatibility and add SYCL runtime libraries
+ // ========================================================================
+ if (Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false) &&
+ !Args.hasArg(options::OPT_nolibsycl)) {
+
+ // ----------------------------------------------------------------------
+ // STEP 1: Validate that static CRT is not being used
+ // ----------------------------------------------------------------------
+ bool HasStaticCRT = false;
+
+ // Check for explicit /MT or /MTd flags
+ if (Args.hasArg(options::OPT__SLASH_MT) ||
+ Args.hasArg(options::OPT__SLASH_MTd)) {
+ HasStaticCRT = true;
+ }
+
+ // Check -fms-runtime-lib flag
+ if (const Arg *A = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
+ StringRef RuntimeLib = A->getValue();
+ if (RuntimeLib == "static" || RuntimeLib == "static_dbg") {
+ HasStaticCRT = true;
+ }
+ }
+
+ // Reject static CRT with SYCL
+ if (HasStaticCRT) {
+ TC.getDriver().Diag(diag::err_drv_sycl_requires_dynamic_crt);
+ // Continue to show other potential errors, but skip SYCL library
+ // linking
+ } else {
+ // --------------------------------------------------------------------
+ // STEP 2: Add SYCL library search path
+ // --------------------------------------------------------------------
+ CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:") +
+ TC.getDriver().Dir + "/../lib"));
+
+ // --------------------------------------------------------------------
+ // STEP 3: Determine if this is a debug build
+ // Only check DYNAMIC CRT flags (/MDd), never static (/MTd)
+ // --------------------------------------------------------------------
+ bool IsDebugBuild = false;
+
+ // Method 1: Check -fms-runtime-lib=dll_dbg
+ if (const Arg *A = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
+ StringRef RuntimeVal = A->getValue();
+ if (RuntimeVal == "dll_dbg")
+ IsDebugBuild = true;
+ }
+
+ // Method 2: Check for /MDd flag (dynamic debug CRT)
+ if (Args.hasArg(options::OPT__SLASH_MDd))
+ IsDebugBuild = true;
+
+ // --------------------------------------------------------------------
+ // STEP 4: Add appropriate SYCL runtime library
+ // Using LLVMSYCL for LLVM naming convention
+ // --------------------------------------------------------------------
+ if (IsDebugBuild) {
+ // Debug build: link debug SYCL runtime
+ CmdArgs.push_back("-defaultlib:LLVMSYCLd.lib");
+ } else {
+ // Release build: link release SYCL runtime
+ CmdArgs.push_back("-defaultlib:LLVMSYCL.lib");
+ }
+
+ // Add msvcrt for SYCL (dynamic CRT)
+ CmdArgs.push_back("-defaultlib:msvcrt");
+ }
+ } else {
+ // Original logic for non-SYCL builds
+ CmdArgs.push_back("-defaultlib:libcmt");
+ }
+
CmdArgs.push_back("-defaultlib:oldnames");
}
@@ -162,6 +262,12 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
if (TC.getVFS().exists(CRTPath))
CmdArgs.push_back(Args.MakeArgString("-libpath:" + CRTPath));
+ // ==========================================================================
+ // SYCL: Suppress LNK4078 warning (multiple .llvm.offloading sections)
+ // ==========================================================================
+ if (Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false))
+ CmdArgs.push_back("/IGNORE:4078");
+
CmdArgs.push_back("-nologo");
if (Args.hasArg(options::OPT_g_Group, options::OPT__SLASH_Z7))
>From 0e6636be0f146b5f0eaa09823b4cd8592235b97c Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Tue, 28 Apr 2026 15:28:43 -0700
Subject: [PATCH 2/5] Add SYCL RT lib path for windows and test.
---
clang/lib/Driver/ToolChains/SYCL.cpp | 65 ++++++++++++++++++++--------
clang/test/Driver/sycl-windows.cpp | 65 ++++++++++++++++++++++++++++
2 files changed, 111 insertions(+), 19 deletions(-)
create mode 100644 clang/test/Driver/sycl-windows.cpp
diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp
index 55a697df5081e..7e7c1d6a31d6a 100644
--- a/clang/lib/Driver/ToolChains/SYCL.cpp
+++ b/clang/lib/Driver/ToolChains/SYCL.cpp
@@ -23,25 +23,52 @@ SYCLInstallationDetector::SYCLInstallationDetector(
// directory in SYCLRTLibPath for use by the linker.
StringRef SysRoot = D.SysRoot;
SmallString<128> DriverDir(D.Dir);
- SmallString<128> LibPath(DriverDir);
- llvm::sys::path::append(LibPath, "..", "lib", HostTriple.str(),
- "libLLVMSYCL.so");
- // Flat lib path for LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF builds,
- // where the library is installed directly in lib/ with no triple subdir.
- SmallString<128> FlatLibPath(DriverDir);
- llvm::sys::path::append(FlatLibPath, "..", "lib", "libLLVMSYCL.so");
-
- if (DriverDir.starts_with(SysRoot) &&
- Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false)) {
- // LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON: library is in lib/<triple>/
- if (D.getVFS().exists(LibPath))
- llvm::sys::path::append(DriverDir, "..", "lib", HostTriple.str());
- // LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF: library is in lib/
- else if (D.getVFS().exists(FlatLibPath))
- llvm::sys::path::append(DriverDir, "..", "lib");
- else
- return; // Neither path exists : broken install, leave SYCLRTLibPath unset
- SYCLRTLibPath = DriverDir;
+
+ if (HostTriple.isOSWindows()) {
+ // ========================================================================
+ // Windows: Check for LLVMSYCL.lib
+ // NOTE: Only checks for release library; debug variant is selected via
+ // CRT flags in MSVC.cpp, not filesystem detection
+ // ========================================================================
+ if (DriverDir.starts_with(SysRoot) &&
+ Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false)) {
+ SmallString<128> LibDir(DriverDir);
+ llvm::sys::path::append(LibDir, "..", "lib");
+
+ // Verify SYCL runtime library exists
+ SmallString<256> SYCLLibPath(LibDir);
+ llvm::sys::path::append(SYCLLibPath, "LLVMSYCL.lib");
+
+ if (D.getVFS().exists(SYCLLibPath)) {
+ SYCLRTLibPath = LibDir;
+ }
+ }
+ } else {
+ // ========================================================================
+ // Linux/Unix: Check for libLLVMSYCL.so (existing logic)
+ // ========================================================================
+ SmallString<128> LibPath(DriverDir);
+ llvm::sys::path::append(LibPath, "..", "lib", HostTriple.str(),
+ "libLLVMSYCL.so");
+ // Flat lib path for LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF builds,
+ // where the library is installed directly in lib/ with no triple subdir.
+ SmallString<128> FlatLibPath(DriverDir);
+ llvm::sys::path::append(FlatLibPath, "..", "lib", "libLLVMSYCL.so");
+
+ if (DriverDir.starts_with(SysRoot) &&
+ Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false)) {
+ // LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON: library is in lib/<triple>/
+ if (D.getVFS().exists(LibPath))
+ llvm::sys::path::append(DriverDir, "..", "lib", HostTriple.str());
+ // LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF: library is in lib/
+ else if (D.getVFS().exists(FlatLibPath))
+ llvm::sys::path::append(DriverDir, "..", "lib");
+ else
+ return; // Neither path exists : broken install, leave SYCLRTLibPath
+ // unset
+
+ SYCLRTLibPath = DriverDir;
+ }
}
}
diff --git a/clang/test/Driver/sycl-windows.cpp b/clang/test/Driver/sycl-windows.cpp
new file mode 100644
index 0000000000000..4fae8da6d0f37
--- /dev/null
+++ b/clang/test/Driver/sycl-windows.cpp
@@ -0,0 +1,65 @@
+/// Windows SYCL runtime library linking tests
+
+
+/// Test 1: Auto-/MD is added when no CRT specified
+// RUN: %clang_cl -### -fsycl --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-AUTO-MD %s
+// CHECK-AUTO-MD: "/MD"
+// CHECK-AUTO-MD: "-defaultlib:LLVMSYCL.lib"
+
+/// Test 2: /MT is rejected with clear error
+// RUN: not %clang_cl -fsycl /MT --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-MT-ERROR %s
+// CHECK-MT-ERROR: error: SYCL requires dynamic C++ runtime
+
+/// Test 3: /MTd is also rejected
+// RUN: not %clang_cl -fsycl /MTd --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-MTD-ERROR %s
+// CHECK-MTD-ERROR: error: SYCL requires dynamic C++ runtime
+
+/// Test 4: /MD uses release library
+// RUN: %clang_cl -### -fsycl /MD --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-RELEASE %s
+// CHECK-RELEASE: "-defaultlib:LLVMSYCL.lib"
+// CHECK-RELEASE-NOT: LLVMSYCLd.lib
+
+/// Test 5: /MDd uses debug library
+// RUN: %clang_cl -### -fsycl /MDd --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-DEBUG %s
+// CHECK-DEBUG: "-defaultlib:LLVMSYCLd.lib"
+// CHECK-DEBUG-NOT: "-defaultlib:LLVMSYCL.lib"
+
+/// Test 6: -fms-runtime-lib=static is rejected
+// RUN: not %clang_cl -fsycl -fms-runtime-lib=static \
+// RUN: --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-STATIC-ERROR %s
+// CHECK-STATIC-ERROR: error: SYCL requires dynamic C++ runtime
+
+/// Test 7: -fms-runtime-lib=dll_dbg uses debug library
+// RUN: %clang_cl -### -fsycl -fms-runtime-lib=dll_dbg \
+// RUN: --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-FLAG-DEBUG %s
+// CHECK-FLAG-DEBUG: "-defaultlib:LLVMSYCLd.lib"
+
+/// Test 8: LNK4078 warning is suppressed
+// RUN: %clang_cl -### -fsycl --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-IGNORE %s
+// CHECK-IGNORE: "/IGNORE:4078"
+
+/// Test 9: -nolibsycl skips library linking and CRT check
+// RUN: %clang_cl -### -fsycl -nolibsycl /MT \
+// RUN: --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-NOLIBSYCL %s
+// CHECK-NOLIBSYCL-NOT: error:
+// CHECK-NOLIBSYCL-NOT: "-defaultlib:LLVMSYCL
+
+/// Test 10: Explicit /MD doesn't cause duplicate flag
+// RUN: %clang_cl -### -fsycl /MD --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-NO-DUP %s
+// CHECK-NO-DUP: "/MD"
+// CHECK-NO-DUP-NOT: "/MD"{{.*}}"/MD"
+
+/// Test 11: Library search path is added
+// RUN: %clang_cl -### -fsycl --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: | FileCheck -check-prefix=CHECK-LIBPATH %s
+// CHECK-LIBPATH: "-libpath:{{.*}}{{[/\\]+}}lib"
>From 4479f02c843373eb3dc7c11f1215124dec3328d4 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Tue, 28 Apr 2026 15:51:35 -0700
Subject: [PATCH 3/5] Update test.
---
clang/lib/Driver/ToolChains/MSVC.cpp | 124 +++++++++++++--------------
clang/test/Driver/sycl-windows.cpp | 9 +-
2 files changed, 66 insertions(+), 67 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 2e052f6e282b1..4b0fa4627980f 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -111,83 +111,81 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}
}
- if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles) &&
- !C.getDriver().IsCLMode() && !C.getDriver().IsFlangMode()) {
+ // ==========================================================================
+ // SYCL: Validate CRT compatibility and add SYCL runtime libraries
+ // This needs to run for both CL mode and regular mode
+ // ==========================================================================
+ if (Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false) &&
+ !Args.hasArg(options::OPT_nolibsycl) &&
+ !Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) {
- // ========================================================================
- // SYCL: Validate CRT compatibility and add SYCL runtime libraries
- // ========================================================================
- if (Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false) &&
- !Args.hasArg(options::OPT_nolibsycl)) {
+ // ------------------------------------------------------------------------
+ // STEP 1: Validate that static CRT is not being used
+ // ------------------------------------------------------------------------
+ bool HasStaticCRT = false;
- // ----------------------------------------------------------------------
- // STEP 1: Validate that static CRT is not being used
- // ----------------------------------------------------------------------
- bool HasStaticCRT = false;
+ // Check for explicit /MT or /MTd flags
+ if (Args.hasArg(options::OPT__SLASH_MT) ||
+ Args.hasArg(options::OPT__SLASH_MTd)) {
+ HasStaticCRT = true;
+ }
- // Check for explicit /MT or /MTd flags
- if (Args.hasArg(options::OPT__SLASH_MT) ||
- Args.hasArg(options::OPT__SLASH_MTd)) {
+ // Check -fms-runtime-lib flag
+ if (const Arg *A = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
+ StringRef RuntimeLib = A->getValue();
+ if (RuntimeLib == "static" || RuntimeLib == "static_dbg") {
HasStaticCRT = true;
}
+ }
- // Check -fms-runtime-lib flag
- if (const Arg *A = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
- StringRef RuntimeLib = A->getValue();
- if (RuntimeLib == "static" || RuntimeLib == "static_dbg") {
- HasStaticCRT = true;
- }
- }
+ // Reject static CRT with SYCL
+ if (HasStaticCRT) {
+ TC.getDriver().Diag(diag::err_drv_sycl_requires_dynamic_crt);
+ // Continue to show other potential errors, but skip SYCL library linking
+ } else {
+ // ----------------------------------------------------------------------
+ // STEP 2: Add SYCL library search path
+ // ----------------------------------------------------------------------
+ CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:") +
+ TC.getDriver().Dir + "/../lib"));
- // Reject static CRT with SYCL
- if (HasStaticCRT) {
- TC.getDriver().Diag(diag::err_drv_sycl_requires_dynamic_crt);
- // Continue to show other potential errors, but skip SYCL library
- // linking
- } else {
- // --------------------------------------------------------------------
- // STEP 2: Add SYCL library search path
- // --------------------------------------------------------------------
- CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:") +
- TC.getDriver().Dir + "/../lib"));
-
- // --------------------------------------------------------------------
- // STEP 3: Determine if this is a debug build
- // Only check DYNAMIC CRT flags (/MDd), never static (/MTd)
- // --------------------------------------------------------------------
- bool IsDebugBuild = false;
-
- // Method 1: Check -fms-runtime-lib=dll_dbg
- if (const Arg *A = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
- StringRef RuntimeVal = A->getValue();
- if (RuntimeVal == "dll_dbg")
- IsDebugBuild = true;
- }
+ // ----------------------------------------------------------------------
+ // STEP 3: Determine if this is a debug build
+ // Only check DYNAMIC CRT flags (/MDd), never static (/MTd)
+ // ----------------------------------------------------------------------
+ bool IsDebugBuild = false;
- // Method 2: Check for /MDd flag (dynamic debug CRT)
- if (Args.hasArg(options::OPT__SLASH_MDd))
+ // Method 1: Check -fms-runtime-lib=dll_dbg
+ if (const Arg *A = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
+ StringRef RuntimeVal = A->getValue();
+ if (RuntimeVal == "dll_dbg")
IsDebugBuild = true;
+ }
- // --------------------------------------------------------------------
- // STEP 4: Add appropriate SYCL runtime library
- // Using LLVMSYCL for LLVM naming convention
- // --------------------------------------------------------------------
- if (IsDebugBuild) {
- // Debug build: link debug SYCL runtime
- CmdArgs.push_back("-defaultlib:LLVMSYCLd.lib");
- } else {
- // Release build: link release SYCL runtime
- CmdArgs.push_back("-defaultlib:LLVMSYCL.lib");
- }
+ // Method 2: Check for /MDd flag (dynamic debug CRT)
+ if (Args.hasArg(options::OPT__SLASH_MDd))
+ IsDebugBuild = true;
- // Add msvcrt for SYCL (dynamic CRT)
- CmdArgs.push_back("-defaultlib:msvcrt");
+ // ----------------------------------------------------------------------
+ // STEP 4: Add appropriate SYCL runtime library
+ // Using LLVMSYCL for LLVM naming convention
+ // ----------------------------------------------------------------------
+ if (IsDebugBuild) {
+ // Debug build: link debug SYCL runtime
+ CmdArgs.push_back("-defaultlib:LLVMSYCLd.lib");
+ } else {
+ // Release build: link release SYCL runtime
+ CmdArgs.push_back("-defaultlib:LLVMSYCL.lib");
}
- } else {
- // Original logic for non-SYCL builds
- CmdArgs.push_back("-defaultlib:libcmt");
+
+ // Add msvcrt for SYCL (dynamic CRT)
+ CmdArgs.push_back("-defaultlib:msvcrt");
}
+ }
+ if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles) &&
+ !C.getDriver().IsCLMode() && !C.getDriver().IsFlangMode()) {
+ CmdArgs.push_back("-defaultlib:libcmt");
CmdArgs.push_back("-defaultlib:oldnames");
}
diff --git a/clang/test/Driver/sycl-windows.cpp b/clang/test/Driver/sycl-windows.cpp
index 4fae8da6d0f37..86c09c4014cff 100644
--- a/clang/test/Driver/sycl-windows.cpp
+++ b/clang/test/Driver/sycl-windows.cpp
@@ -53,11 +53,12 @@
// CHECK-NOLIBSYCL-NOT: error:
// CHECK-NOLIBSYCL-NOT: "-defaultlib:LLVMSYCL
-/// Test 10: Explicit /MD doesn't cause duplicate flag
+/// Test 10: Explicit /MD results in correct libraries
// RUN: %clang_cl -### -fsycl /MD --target=x86_64-pc-windows-msvc %s 2>&1 \
-// RUN: | FileCheck -check-prefix=CHECK-NO-DUP %s
-// CHECK-NO-DUP: "/MD"
-// CHECK-NO-DUP-NOT: "/MD"{{.*}}"/MD"
+// RUN: | FileCheck -check-prefix=CHECK-EXPLICIT-MD %s
+// CHECK-EXPLICIT-MD: clang-linker-wrapper"
+// CHECK-EXPLICIT-MD-SAME: "-defaultlib:LLVMSYCL.lib"
+// CHECK-EXPLICIT-MD-SAME: "-defaultlib:msvcrt"
/// Test 11: Library search path is added
// RUN: %clang_cl -### -fsycl --target=x86_64-pc-windows-msvc %s 2>&1 \
>From 5472767ebbcce78ba79912c4be36d336140548fb Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Tue, 5 May 2026 15:39:42 -0700
Subject: [PATCH 4/5] Embeds SYCL library dependency via `--dependent-lib` at
compiler stage.
---
clang/lib/Driver/ToolChains/Clang.cpp | 26 +++++++++
clang/lib/Driver/ToolChains/MSVC.cpp | 76 ++++++---------------------
clang/lib/Driver/ToolChains/SYCL.cpp | 6 +--
clang/test/Driver/sycl-windows.cpp | 65 +++++++++++++----------
4 files changed, 80 insertions(+), 93 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index cfa3031431498..be6803c2c3b09 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4911,6 +4911,32 @@ static void ProcessVSRuntimeLibrary(const ToolChain &TC, const ArgList &Args,
CmdArgs.push_back("--dependent-lib=oldnames");
}
+ // SYCL: Add SYCL runtime library dependency
+ // SYCL runtime is a required dependency similar to CRT, so we use
+ // --dependent-lib to embed it in the object file metadata
+ if (Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false) &&
+ !Args.hasArg(options::OPT_nolibsycl) &&
+ !Args.hasArg(options::OPT_fms_omit_default_lib)) {
+
+ // Determine debug vs release based on CRT flags
+ bool IsDebugBuild = false;
+
+ // Check -fms-runtime-lib=dll_dbg
+ if (const Arg *A = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
+ StringRef RuntimeVal = A->getValue();
+ if (RuntimeVal == "dll_dbg")
+ IsDebugBuild = true;
+ }
+
+ // Check for /MDd flag (dynamic debug CRT)
+ if (Args.hasArg(options::OPT__SLASH_MDd))
+ IsDebugBuild = true;
+
+ // Add appropriate SYCL runtime library dependency
+ CmdArgs.push_back(IsDebugBuild ? "--dependent-lib=LLVMSYCLd"
+ : "--dependent-lib=LLVMSYCL");
+ }
+
// All Arm64EC object files implicitly add softintrin.lib. This is necessary
// even if the file doesn't actually refer to any of the routines because
// the CRT itself has incomplete dependency markings.
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 4b0fa4627980f..8af89949da7cf 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -85,11 +85,9 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(Args.MakeArgString("--dependent-lib=amath"));
}
- // ==========================================================================
// SYCL: Auto-add /MD if no C++ runtime specified
// SYCL requires dynamic C++ runtime because STL objects cross DLL boundaries
// This must happen BEFORE other CRT handling logic
- // ==========================================================================
if (Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false) &&
!Args.hasArg(options::OPT_nolibsycl) &&
!Args.hasArg(options::OPT_nostdlib) &&
@@ -111,75 +109,31 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}
}
- // ==========================================================================
- // SYCL: Validate CRT compatibility and add SYCL runtime libraries
- // This needs to run for both CL mode and regular mode
- // ==========================================================================
+ // SYCL requires dynamic CRT because STL objects cross DLL boundaries.
+ // Library dependency is added via --dependent-lib at compiler stage.
+ // Here we validate CRT compatibility and add the library search path.
if (Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false) &&
!Args.hasArg(options::OPT_nolibsycl) &&
!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) {
- // ------------------------------------------------------------------------
- // STEP 1: Validate that static CRT is not being used
- // ------------------------------------------------------------------------
- bool HasStaticCRT = false;
+ // Check if static CRT is being used.
+ bool HasStaticCRT = Args.hasArg(options::OPT__SLASH_MT) ||
+ Args.hasArg(options::OPT__SLASH_MTd);
- // Check for explicit /MT or /MTd flags
- if (Args.hasArg(options::OPT__SLASH_MT) ||
- Args.hasArg(options::OPT__SLASH_MTd)) {
- HasStaticCRT = true;
- }
-
- // Check -fms-runtime-lib flag
if (const Arg *A = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
StringRef RuntimeLib = A->getValue();
- if (RuntimeLib == "static" || RuntimeLib == "static_dbg") {
+ if (RuntimeLib == "static" || RuntimeLib == "static_dbg")
HasStaticCRT = true;
- }
}
- // Reject static CRT with SYCL
if (HasStaticCRT) {
TC.getDriver().Diag(diag::err_drv_sycl_requires_dynamic_crt);
- // Continue to show other potential errors, but skip SYCL library linking
} else {
- // ----------------------------------------------------------------------
- // STEP 2: Add SYCL library search path
- // ----------------------------------------------------------------------
- CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:") +
- TC.getDriver().Dir + "/../lib"));
-
- // ----------------------------------------------------------------------
- // STEP 3: Determine if this is a debug build
- // Only check DYNAMIC CRT flags (/MDd), never static (/MTd)
- // ----------------------------------------------------------------------
- bool IsDebugBuild = false;
-
- // Method 1: Check -fms-runtime-lib=dll_dbg
- if (const Arg *A = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
- StringRef RuntimeVal = A->getValue();
- if (RuntimeVal == "dll_dbg")
- IsDebugBuild = true;
- }
-
- // Method 2: Check for /MDd flag (dynamic debug CRT)
- if (Args.hasArg(options::OPT__SLASH_MDd))
- IsDebugBuild = true;
-
- // ----------------------------------------------------------------------
- // STEP 4: Add appropriate SYCL runtime library
- // Using LLVMSYCL for LLVM naming convention
- // ----------------------------------------------------------------------
- if (IsDebugBuild) {
- // Debug build: link debug SYCL runtime
- CmdArgs.push_back("-defaultlib:LLVMSYCLd.lib");
- } else {
- // Release build: link release SYCL runtime
- CmdArgs.push_back("-defaultlib:LLVMSYCL.lib");
- }
-
- // Add msvcrt for SYCL (dynamic CRT)
- CmdArgs.push_back("-defaultlib:msvcrt");
+ // Add library search path so linker can find LLVMSYCL[d].lib.
+ SmallString<128> LibPath(TC.getDriver().Dir);
+ llvm::sys::path::append(LibPath, "..", "lib");
+ CmdArgs.push_back(
+ Args.MakeArgString(Twine("-libpath:") + LibPath));
}
}
@@ -260,9 +214,9 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
if (TC.getVFS().exists(CRTPath))
CmdArgs.push_back(Args.MakeArgString("-libpath:" + CRTPath));
- // ==========================================================================
- // SYCL: Suppress LNK4078 warning (multiple .llvm.offloading sections)
- // ==========================================================================
+ // SYCL offload compilation creates .llvm.offloading sections in each object
+ // file to store device code and metadata. Suppress linker warning about
+ // multiple sections with different attributes (LNK4078).
if (Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false))
CmdArgs.push_back("/IGNORE:4078");
diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp
index 7e7c1d6a31d6a..3398036f3b54a 100644
--- a/clang/lib/Driver/ToolChains/SYCL.cpp
+++ b/clang/lib/Driver/ToolChains/SYCL.cpp
@@ -25,18 +25,16 @@ SYCLInstallationDetector::SYCLInstallationDetector(
SmallString<128> DriverDir(D.Dir);
if (HostTriple.isOSWindows()) {
- // ========================================================================
// Windows: Check for LLVMSYCL.lib
// NOTE: Only checks for release library; debug variant is selected via
// CRT flags in MSVC.cpp, not filesystem detection
- // ========================================================================
if (DriverDir.starts_with(SysRoot) &&
Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false)) {
SmallString<128> LibDir(DriverDir);
llvm::sys::path::append(LibDir, "..", "lib");
// Verify SYCL runtime library exists
- SmallString<256> SYCLLibPath(LibDir);
+ SmallString<128> SYCLLibPath(LibDir);
llvm::sys::path::append(SYCLLibPath, "LLVMSYCL.lib");
if (D.getVFS().exists(SYCLLibPath)) {
@@ -44,9 +42,7 @@ SYCLInstallationDetector::SYCLInstallationDetector(
}
}
} else {
- // ========================================================================
// Linux/Unix: Check for libLLVMSYCL.so (existing logic)
- // ========================================================================
SmallString<128> LibPath(DriverDir);
llvm::sys::path::append(LibPath, "..", "lib", HostTriple.str(),
"libLLVMSYCL.so");
diff --git a/clang/test/Driver/sycl-windows.cpp b/clang/test/Driver/sycl-windows.cpp
index 86c09c4014cff..14ec16fe4f63e 100644
--- a/clang/test/Driver/sycl-windows.cpp
+++ b/clang/test/Driver/sycl-windows.cpp
@@ -1,66 +1,77 @@
/// Windows SYCL runtime library linking tests
+//
+// NOTE: SYCL runtime library dependency is added at compiler stage via
+// --dependent-lib (embedded in object file), similar to CRT libraries.
+// Tests check for --dependent-lib in compiler (-cc1) output and
+// -libpath: in linker output.
-
-/// Test 1: Auto-/MD is added when no CRT specified
-// RUN: %clang_cl -### -fsycl --target=x86_64-pc-windows-msvc %s 2>&1 \
+/// Test 1: Auto-/MD and release library dependency
+// RUN: %clang_cl -### -fsycl --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-AUTO-MD %s
-// CHECK-AUTO-MD: "/MD"
-// CHECK-AUTO-MD: "-defaultlib:LLVMSYCL.lib"
+// CHECK-AUTO-MD: "-cc1"
+// CHECK-AUTO-MD: "--dependent-lib=LLVMSYCL"
+// CHECK-AUTO-MD: clang-linker-wrapper"
+// CHECK-AUTO-MD-SAME: "/MD"
+// CHECK-AUTO-MD-SAME: "-libpath:{{.*}}{{[/\\]+}}lib"
/// Test 2: /MT is rejected with clear error
-// RUN: not %clang_cl -fsycl /MT --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: not %clang_cl -fsycl /MT --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-MT-ERROR %s
// CHECK-MT-ERROR: error: SYCL requires dynamic C++ runtime
/// Test 3: /MTd is also rejected
-// RUN: not %clang_cl -fsycl /MTd --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: not %clang_cl -fsycl /MTd --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-MTD-ERROR %s
// CHECK-MTD-ERROR: error: SYCL requires dynamic C++ runtime
/// Test 4: /MD uses release library
-// RUN: %clang_cl -### -fsycl /MD --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: %clang_cl -### -fsycl /MD --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-RELEASE %s
-// CHECK-RELEASE: "-defaultlib:LLVMSYCL.lib"
-// CHECK-RELEASE-NOT: LLVMSYCLd.lib
+// CHECK-RELEASE: "-cc1"
+// CHECK-RELEASE: "--dependent-lib=LLVMSYCL"
+// CHECK-RELEASE-NOT: LLVMSYCLd
/// Test 5: /MDd uses debug library
-// RUN: %clang_cl -### -fsycl /MDd --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: %clang_cl -### -fsycl /MDd --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-DEBUG %s
-// CHECK-DEBUG: "-defaultlib:LLVMSYCLd.lib"
-// CHECK-DEBUG-NOT: "-defaultlib:LLVMSYCL.lib"
+// CHECK-DEBUG: "-cc1"
+// CHECK-DEBUG: "--dependent-lib=LLVMSYCLd"
+// CHECK-DEBUG-NOT: "--dependent-lib=LLVMSYCL"
/// Test 6: -fms-runtime-lib=static is rejected
// RUN: not %clang_cl -fsycl -fms-runtime-lib=static \
-// RUN: --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-STATIC-ERROR %s
// CHECK-STATIC-ERROR: error: SYCL requires dynamic C++ runtime
/// Test 7: -fms-runtime-lib=dll_dbg uses debug library
// RUN: %clang_cl -### -fsycl -fms-runtime-lib=dll_dbg \
-// RUN: --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-FLAG-DEBUG %s
-// CHECK-FLAG-DEBUG: "-defaultlib:LLVMSYCLd.lib"
+// CHECK-FLAG-DEBUG: "-cc1"
+// CHECK-FLAG-DEBUG: "--dependent-lib=LLVMSYCLd"
/// Test 8: LNK4078 warning is suppressed
-// RUN: %clang_cl -### -fsycl --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: %clang_cl -### -fsycl --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-IGNORE %s
+// CHECK-IGNORE: clang-linker-wrapper"
// CHECK-IGNORE: "/IGNORE:4078"
-/// Test 9: -nolibsycl skips library linking and CRT check
+/// Test 9: -nolibsycl skips library dependency and CRT check
// RUN: %clang_cl -### -fsycl -nolibsycl /MT \
-// RUN: --target=x86_64-pc-windows-msvc %s 2>&1 \
+// RUN: --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-NOLIBSYCL %s
// CHECK-NOLIBSYCL-NOT: error:
-// CHECK-NOLIBSYCL-NOT: "-defaultlib:LLVMSYCL
+// CHECK-NOLIBSYCL-NOT: "--dependent-lib=LLVMSYCL"
-/// Test 10: Explicit /MD results in correct libraries
-// RUN: %clang_cl -### -fsycl /MD --target=x86_64-pc-windows-msvc %s 2>&1 \
+/// Test 10: Explicit /MD results in correct library
+// RUN: %clang_cl -### -fsycl /MD --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-EXPLICIT-MD %s
-// CHECK-EXPLICIT-MD: clang-linker-wrapper"
-// CHECK-EXPLICIT-MD-SAME: "-defaultlib:LLVMSYCL.lib"
-// CHECK-EXPLICIT-MD-SAME: "-defaultlib:msvcrt"
+// CHECK-EXPLICIT-MD: "-cc1"
+// CHECK-EXPLICIT-MD: "--dependent-lib=LLVMSYCL"
-/// Test 11: Library search path is added
-// RUN: %clang_cl -### -fsycl --target=x86_64-pc-windows-msvc %s 2>&1 \
+/// Test 11: Library search path is added at linker stage
+// RUN: %clang_cl -### -fsycl --target=x86_64-pc-windows-msvc -- %s 2>&1 \
// RUN: | FileCheck -check-prefix=CHECK-LIBPATH %s
+// CHECK-LIBPATH: clang-linker-wrapper"
// CHECK-LIBPATH: "-libpath:{{.*}}{{[/\\]+}}lib"
>From 5536e79a31d08d38de99abf6b7356f110f91806f Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Tue, 5 May 2026 15:45:23 -0700
Subject: [PATCH 5/5] Clang format.
---
clang/lib/Driver/ToolChains/MSVC.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 8af89949da7cf..66afcc45c0ed5 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -132,8 +132,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
// Add library search path so linker can find LLVMSYCL[d].lib.
SmallString<128> LibPath(TC.getDriver().Dir);
llvm::sys::path::append(LibPath, "..", "lib");
- CmdArgs.push_back(
- Args.MakeArgString(Twine("-libpath:") + LibPath));
+ CmdArgs.push_back(Args.MakeArgString(Twine("-libpath:") + LibPath));
}
}
More information about the cfe-commits
mailing list