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
