Author: Nick Sarnie
Date: 2026-02-23T19:53:32Z
New Revision: 3f742e98794a855e9fbbc60f6c14eb3277d2f030

URL: 
https://github.com/llvm/llvm-project/commit/3f742e98794a855e9fbbc60f6c14eb3277d2f030
DIFF: 
https://github.com/llvm/llvm-project/commit/3f742e98794a855e9fbbc60f6c14eb3277d2f030.diff

LOG: [Clang][Driver][SPIRV] Support LTO through the llvm-lto tool (#182347)

There is no SPIR-V linker that supports LTO and a proposal to support
basic SPIR-V linking in `lld` was
[rejected](https://github.com/llvm/llvm-project/pull/178749), 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.

---------

Signed-off-by: Nick Sarnie <[email protected]>

Added: 
    clang/test/Driver/spirv-lto.c

Modified: 
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Driver/ToolChains/SPIRV.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 195f05d61464f..2a7fb6033b2ac 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -9337,8 +9337,10 @@ void LinkerWrapper::ConstructJob(Compilation &C, const 
JobAction &JA,
         LinkerArgs.emplace_back("-lompdevice");
 
       // For SPIR-V some functions will be defined by the runtime so allow
-      // unresolved symbols.
-      if (TC->getTriple().isSPIRV())
+      // unresolved symbols in `spirv-link`. `spirv-link` isn't called in LTO
+      // mode so restrict this flag to normal compilation.
+      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..a59bd05cac0cf 100644
--- a/clang/lib/Driver/ToolChains/SPIRV.cpp
+++ b/clang/lib/Driver/ToolChains/SPIRV.cpp
@@ -147,17 +147,31 @@ 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))
+  // TODO: Consider moving SPIR-V linking to a separate tool.
+  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 +185,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..64f5f30a8a1eb
--- /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.c
+// RUN: touch %t/b.c
+// RUN: touch %t/a.o
+// RUN: touch %t/b.o
+
+// RUN: %clang -### --target=spirv64 -flto %t/a.c %t/b.c  -Xlinker 
--disable-verify 2>&1 | FileCheck --check-prefix=CHECK-POSITIVE-TOOL %s
+// RUN: %clang -ccc-print-phases --target=spirv64 -flto %t/a.c %t/b.c 2>&1 | 
FileCheck --check-prefix=CHECK-POSITIVE-PHASES %s
+// RUN: not %clang -### --target=spirv64 -flto %t/a.c %t/b.c --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.c", c
+// CHECK-POSITIVE-PHASES: 1: preprocessor, {0}, cpp-output
+// CHECK-POSITIVE-PHASES: 2: compiler, {1}, ir
+// CHECK-POSITIVE-PHASES: 3: backend, {2}, lto-bc
+// CHECK-POSITIVE-PHASES: 4: input, "{{.*}}b.c", c
+// CHECK-POSITIVE-PHASES: 5: preprocessor, {4}, 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