https://github.com/sarnex created 
https://github.com/llvm/llvm-project/pull/182347

There is no SPIR-V linker that supports LTO and a proposal to support basic 
SPIR-V linking in `lld` was rejected, so support a basic version of LTO just by 
calling `llvm-lto` directly from the SPIR-V Toolchain.  `-Xlinker` can be used 
to specify flags to `llvm-lto`. 

This should be enough for our use case.

There is also the `llvm-lto2` tool, but that requires a list of symbol 
resolutions in the command line, and we can't compute that in the driver.



>From e73e52d14cee7a44fe27fa15b381039f2a1ea232 Mon Sep 17 00:00:00 2001
From: Nick Sarnie <[email protected]>
Date: Thu, 19 Feb 2026 11:15:17 -0800
Subject: [PATCH] [Clang][Driver][SPIRV] Support LTO through the llvm-lto tool

Signed-off-by: Nick Sarnie <[email protected]>
---
 clang/lib/Driver/ToolChains/Clang.cpp |  3 ++-
 clang/lib/Driver/ToolChains/SPIRV.cpp | 27 +++++++++++++++------
 clang/test/Driver/spirv-lto.c         | 35 +++++++++++++++++++++++++++
 3 files changed, 57 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/Driver/spirv-lto.c

diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 8aa2c595e2dea..03995f538d33a 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -9307,7 +9307,8 @@ void LinkerWrapper::ConstructJob(Compilation &C, const 
JobAction &JA,
 
       // For SPIR-V some functions will be defined by the runtime so allow
       // unresolved symbols.
-      if (TC->getTriple().isSPIRV())
+      if (TC->getTriple().isSPIRV() && !C.getDriver().isUsingLTO() &&
+          !C.getDriver().isUsingOffloadLTO())
         LinkerArgs.emplace_back("--allow-partial-linkage");
 
       // Forward all of these to the appropriate toolchain.
diff --git a/clang/lib/Driver/ToolChains/SPIRV.cpp 
b/clang/lib/Driver/ToolChains/SPIRV.cpp
index c1ccb1e7d8508..762f5d70ad4b6 100644
--- a/clang/lib/Driver/ToolChains/SPIRV.cpp
+++ b/clang/lib/Driver/ToolChains/SPIRV.cpp
@@ -147,17 +147,30 @@ void SPIRV::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
   const ToolChain &ToolChain = getToolChain();
   std::string Linker = ToolChain.GetProgramPath(getShortName());
   ArgStringList CmdArgs;
-  AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);
+  AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
 
   CmdArgs.push_back("-o");
   CmdArgs.push_back(Output.getFilename());
 
-  // Use of --sycl-link will call the clang-sycl-linker instead of
-  // the default linker (spirv-link).
-  if (Args.hasArg(options::OPT_sycl_link))
+  if (C.getDriver().isUsingLTO()) {
+    // Implement limited LTO support through llvm-lto.
+    if (Args.hasArg(options::OPT_sycl_link)) {
+      // For unsupported cases, throw the same error as when LTO isn't 
supported
+      // at all.
+      C.getDriver().Diag(clang::diag::err_drv_no_linker_llvm_support)
+          << ToolChain.getTriple().getTriple();
+      return;
+    }
+    Linker = ToolChain.GetProgramPath("llvm-lto");
+    // Disable internalization, otherwise GlobalDCE will optimize everything
+    // out.
+    CmdArgs.push_back("-enable-lto-internalization=false");
+  } else if (Args.hasArg(options::OPT_sycl_link)) {
+    // Use of --sycl-link will call the clang-sycl-linker instead of
+    // the default linker (spirv-link).
     Linker = ToolChain.GetProgramPath("clang-sycl-linker");
-  else if (!llvm::sys::fs::can_execute(Linker) &&
-           !C.getArgs().hasArg(clang::options::OPT__HASH_HASH_HASH)) {
+  } else if (!llvm::sys::fs::can_execute(Linker) &&
+             !C.getArgs().hasArg(clang::options::OPT__HASH_HASH_HASH)) {
     C.getDriver().Diag(clang::diag::err_drv_no_spv_tools) << getShortName();
     return;
   }
@@ -171,7 +184,7 @@ SPIRVToolChain::SPIRVToolChain(const Driver &D, const 
llvm::Triple &Triple,
     : ToolChain(D, Triple, Args) {
   // TODO: Revisit need/use of --sycl-link option once SYCL toolchain is
   // available and SYCL linking support is moved there.
-  NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link);
+  NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link) || D.isUsingLTO();
 
   // Lookup binaries into the driver directory.
   getProgramPaths().push_back(getDriver().Dir);
diff --git a/clang/test/Driver/spirv-lto.c b/clang/test/Driver/spirv-lto.c
new file mode 100644
index 0000000000000..e85fb643351a9
--- /dev/null
+++ b/clang/test/Driver/spirv-lto.c
@@ -0,0 +1,35 @@
+// Check SPIR-V support for LTO
+// RUN: mkdir -p %t
+// RUN: touch %t/a.cpp
+// RUN: touch %t/b.cpp
+// RUN: touch %t/a.o
+// RUN: touch %t/b.o
+
+// RUN: %clang -### --target=spirv64 -flto %t/a.cpp %t/b.cpp  -Xlinker 
--disable-verify 2>&1 | FileCheck --check-prefix=CHECK-POSITIVE-TOOL %s
+// RUN: %clang -ccc-print-phases --target=spirv64 -flto %t/a.cpp %t/b.cpp 2>&1 
| FileCheck --check-prefix=CHECK-POSITIVE-PHASES %s
+// RUN: not %clang -### --target=spirv64 -flto %t/a.cpp %t/b.cpp --sycl-link 
2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+
+// RUN: %clang -### --target=spirv64 -flto %t/a.o %t/b.o  -Xlinker 
--disable-verify 2>&1 | FileCheck --check-prefix=CHECK-POSITIVE-TOOL-OBJ %s
+// RUN: %clang -ccc-print-phases --target=spirv64 -flto %t/a.o %t/b.o 2>&1 | 
FileCheck --check-prefix=CHECK-POSITIVE-PHASES-OBJ %s
+// RUN: not %clang -### --target=spirv64 -flto %t/a.o %t/b.o --sycl-link 2>&1 
| FileCheck --check-prefix=CHECK-ERROR %s
+
+// CHECK-POSITIVE-TOOL: llvm-lto{{.*}} "{{.*}}/a-{{.*}}.o" "{{.*}}/b-{{.*}}.o" 
"--disable-verify" "-o" "a.out" "-enable-lto-internalization=false"
+
+// CHECK-POSITIVE-PHASES: 0: input, "{{.*}}/a.cpp", c++
+// CHECK-POSITIVE-PHASES: 1: preprocessor, {0}, c++-cpp-output
+// CHECK-POSITIVE-PHASES: 2: compiler, {1}, ir
+// CHECK-POSITIVE-PHASES: 3: backend, {2}, lto-bc
+// CHECK-POSITIVE-PHASES: 4: input, "{{.*}}/b.cpp", c++
+// CHECK-POSITIVE-PHASES: 5: preprocessor, {4}, c++-cpp-output
+// CHECK-POSITIVE-PHASES: 6: compiler, {5}, ir
+// CHECK-POSITIVE-PHASES: 7: backend, {6}, lto-bc
+// CHECK-POSITIVE-PHASES: 8: linker, {3, 7}, image
+
+// CHECK-POSITIVE-TOOL-OBJ:  llvm-lto{{.*}} "{{.*}}/a.o" "{{.*}}/b.o" 
"--disable-verify" "-o" "a.out" "-enable-lto-internalization=false"
+
+// CHECK-POSITIVE-PHASES-OBJ: 0: input, "{{.*}}/a.o", object
+// CHECK-POSITIVE-PHASES-OBJ: 1: input, "{{.*}}/b.o", object
+// CHECK-POSITIVE-PHASES-OBJ: 2: linker, {0, 1}, image
+
+// CHECK-ERROR: 'spirv64': unable to pass LLVM bit-code files to linker
+

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to