[lld] [RFC][lld][SPIRV] Add support for SPIR-V LTO (PR #178749)
Joseph Huber via llvm-commits
llvm-commits at lists.llvm.org
Tue Feb 17 09:11:12 PST 2026
================
@@ -0,0 +1,163 @@
+//===- LTO.cpp ------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LTO.h"
+#include "Config.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/TargetOptionsCommandFlags.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/LTO/Config.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/Support/Caching.h"
+#include "llvm/Support/CodeGen.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::spirv;
+
+static lto::Config createConfig(Config &config) {
+ lto::Config c;
+ c.OptLevel = config.ltoOptLevel;
+ c.DisableVerify = false;
+ c.DiagHandler = diagnosticHandler;
+ c.DefaultTriple = config.targetTriple;
+ c.Options = lld::initTargetOptionsFromCodeGenFlags();
+ c.RelocModel = Reloc::Static;
+ c.CodeModel = getCodeModelFromCMModel();
+ c.HasWholeProgramVisibility = true;
+ c.CGFileType = CodeGenFileType::ObjectFile;
+ c.OverrideTriple = config.targetTriple;
+ return c;
+}
+
+BitcodeCompiler::BitcodeCompiler(Config &Config) : config(Config) {
+ lto::ThinBackend backend = lto::createInProcessThinBackend(
+ llvm::heavyweight_hardware_concurrency(1));
+ ltoObj = std::make_unique<lto::LTO>(createConfig(config), backend, 1);
+}
+
+void BitcodeCompiler::add(MemoryBufferRef mb) {
+ Expected<std::unique_ptr<lto::InputFile>> objOrErr =
+ lto::InputFile::create(mb);
+ if (!objOrErr) {
+ error("failed to parse bitcode: " + toString(objOrErr.takeError()));
+ return;
+ }
+ std::unique_ptr<lto::InputFile> &obj = *objOrErr;
+
+ std::vector<lto::SymbolResolution> resolutions;
+ resolutions.reserve(obj->symbols().size());
+ for (const lto::InputFile::Symbol &sym : obj->symbols()) {
+ lto::SymbolResolution res;
+ res.Prevailing = !sym.isUndefined();
+ res.FinalDefinitionInLinkageUnit = !sym.isUndefined();
+ res.VisibleToRegularObj = true;
+ res.ExportDynamic = false;
+ res.LinkerRedefined = false;
+ resolutions.push_back(res);
+ }
+
+ if (Error err = ltoObj->add(std::move(obj), std::move(resolutions)))
+ error("failed to add bitcode: " + toString(std::move(err)));
+}
+
+std::vector<char> BitcodeCompiler::compile() {
+ unsigned maxTasks = ltoObj->getMaxTasks();
+ buf.resize(maxTasks);
+ tempFiles.resize(maxTasks);
+
+ // Run LTO to generate SPIR-V modules
+ if (Error err = ltoObj->run([&](size_t task, const Twine &moduleName) {
+ return std::make_unique<CachedFileStream>(
+ std::make_unique<raw_svector_ostream>(buf[task]));
+ })) {
+ error("LTO failed: " + toString(std::move(err)));
+ return {};
+ }
+
+ // Save SPIR-V modules to temp files for spirv-link
+ std::vector<std::string> spirvFiles;
+ for (unsigned i = 0; i < maxTasks; ++i) {
+ if (buf[i].empty())
+ continue;
+
+ SmallString<128> tempPath;
+ std::error_code ec = sys::fs::createTemporaryFile("lto", "spv", tempPath);
+ if (ec) {
+ error("failed to create temp file: " + ec.message());
+ return {};
+ }
+
+ raw_fd_ostream os(tempPath, ec, sys::fs::OF_None);
+ if (ec) {
+ error("failed to open temp file: " + ec.message());
+ return {};
+ }
+ os.write(buf[i].data(), buf[i].size());
+ os.close();
+
+ tempFiles[i] = tempPath.str().str();
+ spirvFiles.push_back(tempFiles[i]);
+ }
+
+ if (spirvFiles.empty()) {
+ error("LTO produced no output");
+ return {};
+ }
+
+ // Use spirv-link to do the actual linking
----------------
jhuber6 wrote:
So, does this currently accept `spir-v` files? Presumably if we only look LLVM-IR we'd just have the backend emit a single file. Also if we're doing that we should make this accept `--lto-emit-llvm` or whatever it's called similarly to the other targets.
Obviously a tool dependency like this isn't ideal, hopefully in the future LLVM has the LLVM -> SPIR -> LLVM pipeline in-tree but I can see this sitting for some time.
https://github.com/llvm/llvm-project/pull/178749
More information about the llvm-commits
mailing list