================ @@ -1,265 +1,311 @@ -//===- Dtlto.cpp - Distributed ThinLTO implementation --------------------===// +//===- DTLTO.cpp - Distributed ThinLTO implementation ---------------------===// // // 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 // //===----------------------------------------------------------------------===// -// // \file // This file implements support functions for Distributed ThinLTO, focusing on -// preparing input files for distribution. +// preparing complilation jobs for distribution. // //===----------------------------------------------------------------------===// #include "llvm/DTLTO/DTLTO.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" -#include "llvm/BinaryFormat/Magic.h" #include "llvm/LTO/LTO.h" -#include "llvm/Object/Archive.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBufferRef.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" -#include "llvm/Support/Signals.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/raw_ostream.h" -#ifdef _WIN32 -#include "llvm/Support/Windows/WindowsSupport.h" -#endif #include <string> using namespace llvm; -namespace { - -// Saves the content of Buffer to Path overwriting any existing file. -Error save(StringRef Buffer, StringRef Path) { - std::error_code EC; - raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::OF_None); - if (EC) - return createStringError(inconvertibleErrorCode(), - "Failed to create file %s: %s", Path.data(), - EC.message().c_str()); - OS.write(Buffer.data(), Buffer.size()); - if (OS.has_error()) - return createStringError(inconvertibleErrorCode(), - "Failed writing to file %s", Path.data()); - return Error::success(); -} +// Remove temporary files created to enable distribution. +void lto::DTLTO::cleanup() { + if (!SaveTemps) { + // Remove one file, report error if any. + auto removeFile = [](StringRef FileName) -> void { + std::error_code EC = sys::fs::remove(FileName, true); + if (EC && + EC != std::make_error_code(std::errc::no_such_file_or_directory)) + errs() << "warning: could not remove the file '" << FileName + << "': " << EC.message() << "\n"; + }; -// Saves the content of Input to Path overwriting any existing file. -Error save(lto::InputFile *Input, StringRef Path) { - MemoryBufferRef MB = Input->getFileBuffer(); - return save(MB.getBuffer(), Path); + TimeTraceScope JobScope("Remove DTLTO temporary files"); + for (const auto &Name : CleanupList) + removeFile(Name); + } + Base::cleanup(); } -// Normalize and save a path. Aside from expanding Windows 8.3 short paths, -// no other normalization is currently required here. These paths are -// machine-local and break distribution systems; other normalization is -// handled by the DTLTO distributors. -Expected<StringRef> normalizePath(StringRef Path, StringSaver &Saver) { -#if defined(_WIN32) - if (Path.empty()) - return Path; - SmallString<256> Expanded; - if (std::error_code EC = llvm::sys::windows::makeLongFormPath(Path, Expanded)) - return createStringError(inconvertibleErrorCode(), - "Normalization failed for path %s: %s", - Path.str().c_str(), EC.message().c_str()); - return Saver.save(Expanded.str()); -#else - return Saver.save(Path); -#endif -} +// Runs the DTLTO thin link phase, producing per-module summary indices, +// import lists, and cache keys for distribution. +Error lto::DTLTO::performThinLink() { + auto ThinIndexBackend = lto::createWriteIndexesThinBackend( + hardware_concurrency(), "", "", "", true, nullptr, nullptr); + setThinBackend(ThinIndexBackend); + setLTOMode(lto::LTO::LTOKind::LTOK_UnifiedThin); -// Compute the file path for a thin archive member. -// -// For thin archives, an archive member name is typically a file path relative -// to the archive file's directory. This function resolves that path. -SmallString<256> computeThinArchiveMemberPath(StringRef ArchivePath, - StringRef MemberName) { - assert(!ArchivePath.empty() && "An archive file path must be non empty."); - SmallString<256> MemberPath; - if (sys::path::is_relative(MemberName)) { - MemberPath = sys::path::parent_path(ArchivePath); - sys::path::append(MemberPath, MemberName); - } else - MemberPath = MemberName; - sys::path::remove_dots(MemberPath, /*remove_dot_dot=*/true); - return MemberPath; + size_t NumTasks = getMaxTasks(); + SummaryIndexFiles.resize(NumTasks); + ImportsFilesLists.resize(NumTasks); + CacheKeysList.resize(NumTasks); + + lto::Config &Cfg = getConfig(); + Cfg.OnSummaryIndexStoreCb = + [&](size_t task) -> std::unique_ptr<raw_svector_ostream> { + return std::make_unique<raw_svector_ostream>(SummaryIndexFiles[task]); + }; + Cfg.OnCacheKeyStoreCb = [&](size_t task) -> std::string & { + return CacheKeysList[task]; + }; + Cfg.OnImportsListStoreCb = [&](size_t task) -> std::vector<std::string> & { + return ImportsFilesLists[task]; + }; + + return Base::run(AddStreamFunc, {}); } -} // namespace +// Runs the DTLTO pipeline. +LLVM_ABI Error lto::DTLTO::run(AddStreamFn AddStream, FileCache CacheParam) { + scope_exit CleanUp([this]() { cleanup(); }); -// Determines if a file at the given path is a thin archive file. -// -// This function uses a cache to avoid repeatedly reading the same file. -// It reads only the header portion (magic bytes) of the file to identify -// the archive type. -Expected<bool> lto::DTLTO::isThinArchive(const StringRef ArchivePath) { - // Return cached result if available. - auto Cached = ArchiveIsThinCache.find(ArchivePath); - if (Cached != ArchiveIsThinCache.end()) - return Cached->second; - - uint64_t FileSize = -1; - std::error_code EC = sys::fs::file_size(ArchivePath, FileSize); - if (EC) - return createStringError(inconvertibleErrorCode(), - "Failed to get file size from archive %s: %s", - ArchivePath.data(), EC.message().c_str()); - if (FileSize < sizeof(object::ThinArchiveMagic)) - return createStringError(inconvertibleErrorCode(), - "Archive file size is too small %s", - ArchivePath.data()); - - // Read only the first few bytes containing the magic signature. - ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFileSlice( - ArchivePath, sizeof(object::ThinArchiveMagic), 0); - if ((EC = MBOrErr.getError())) - return createStringError(inconvertibleErrorCode(), - "Failed to read from archive %s: %s", - ArchivePath.data(), EC.message().c_str()); - - StringRef Buf = (*MBOrErr)->getBuffer(); - if (file_magic::archive != identify_magic(Buf)) - return createStringError(inconvertibleErrorCode(), - "Unknown format for archive %s", - ArchivePath.data()); - - bool IsThin = Buf.starts_with(object::ThinArchiveMagic); - - // Cache the result. - ArchiveIsThinCache[ArchivePath] = IsThin; - - return IsThin; + AddStreamFunc = AddStream; + Cache = std::move(CacheParam); + Conf.Dtlto = 1; + UID = itostr(sys::Process::getProcessId()); + + if (Error Err = performThinLink()) + return Err; + + ThinLTOTaskOffset = RegularLTO.ParallelCodeGenParallelismLevel; + DistributorParams.TargetTriple = RegularLTO.CombinedModule->getTargetTriple(); + + if (Error Err = prepareDtltoJobs()) + return Err; + if (Error Err = handleArchiveInputs()) + return Err; + if (Error Err = performCodegen()) + return Err; + if (Error Err = addObjectFilesToLink()) + return Err; + return Error::success(); } -// Add an input file and prepare it for distribution. -// -// This function performs the following tasks: -// 1. Add the input file to the LTO object's list of input files. -// 2. For individual bitcode file inputs on Windows only, overwrite the module -// ID with a normalized path to remove short 8.3 form components. -// 3. For thin archive members, overwrite the module ID with the path -// (normalized on Windows) to the member file on disk. -// 4. For archive members and FatLTO objects, overwrite the module ID with a -// unique path (normalized on Windows) naming a file that will contain the -// member content. The file is created and populated later (see -// serializeInputs()). -Expected<std::shared_ptr<lto::InputFile>> -lto::DTLTO::addInput(std::unique_ptr<InputFile> InputPtr) { - TimeTraceScope TimeScope("Add input for DTLTO"); - - // Add the input file to the LTO object. - InputFiles.emplace_back(InputPtr.release()); - auto &Input = InputFiles.back(); - BitcodeModule &BM = Input->getPrimaryBitcodeModule(); - - auto setIdFromPath = [&](StringRef Path) -> Error { - auto N = normalizePath(Path, Saver); - if (!N) - return N.takeError(); - BM.setModuleIdentifier(*N); +// Probes the LTO cache for a compiled native object for the given job. +Error lto::DTLTO::checkCacheHit(Job &J) { + if (!Cache.isValid()) return Error::success(); - }; - StringRef ArchivePath = Input->getArchivePath(); - - // In most cases, the module ID already points to an individual bitcode file - // on disk, so no further preparation for distribution is required. However, - // on Windows we overwite the module ID to expand Windows 8.3 short form - // paths. These paths are machine-local and break distribution systems; other - // normalization is handled by the DTLTO distributors. - if (ArchivePath.empty() && !Input->isFatLTOObject()) { -#if defined(_WIN32) - if (Error E = setIdFromPath(Input->getName())) - return std::move(E); -#endif - return Input; + auto CacheAddStreamExp = Cache(J.Task, J.CacheKey, J.ModuleID); + if (Error Err = CacheAddStreamExp.takeError()) + return Err; + AddStreamFn &CacheAddStream = *CacheAddStreamExp; + // If CacheAddStream is null, we have a cache hit and at this point + // object file is already passed back to the linker. + if (!CacheAddStream) { + J.Cached = true; // Cache hit, mark the job as cached. + CachedJobs.fetch_add(1); + } else { + // If CacheAddStream is not null, we have a cache miss and we need to + // run the backend for codegen. Save cache 'add stream' + // function for a later use. + J.CacheAddStream = std::move(CacheAddStream); } + return Error::success(); +} - // For a member of a thin archive that is not a FatLTO object, there is an - // existing file on disk that can be used, so we can avoid having to - // serialize. - Expected<bool> UseThinMember = - Input->isFatLTOObject() ? false : isThinArchive(ArchivePath); - if (!UseThinMember) - return UseThinMember.takeError(); - if (*UseThinMember) { - // For thin archives, use the path to the actual member file on disk. - auto MemberPath = - computeThinArchiveMemberPath(ArchivePath, Input->getMemberName()); - if (Error E = setIdFromPath(MemberPath)) - return std::move(E); - return Input; - } +// Prepares a single DTLTO backend compilation job for a ThinLTO module. +Error lto::DTLTO::prepareDtltoJob(StringRef ModulePath, unsigned Task) { + assert(Task >= ThinLTOTaskOffset && Task - ThinLTOTaskOffset < Jobs.size() && + "Task index out of range for Jobs"); + assert(Task < SummaryIndexFiles.size() && "Task index out of range"); + + SString ObjFilePath = + sys::path::parent_path(DistributorParams.LinkerOutputFile); + sys::path::append(ObjFilePath, sys::path::stem(ModulePath) + "." + + itostr(Task) + "." + UID + ".native.o"); + + SString SummaryIndexPathStr = ObjFilePath; + SummaryIndexPathStr += ".thinlto.bc"; + SString ImportsPathStr = ModulePath; + ImportsPathStr += ".imports"; - // A new file on disk will be needed for archive members and FatLTO objects. - Input->setSerializeForDistribution(true); + Job &J = Jobs[Task - ThinLTOTaskOffset]; + J = {Task, + ModulePath, + Saver.save(ObjFilePath.str()), + Saver.save(SummaryIndexPathStr.str()), + Saver.save(ImportsPathStr.str()), + ImportsFilesLists[Task], + CacheKeysList[Task], + nullptr, + false}; - // Get the normalized output directory, if we haven't already. - if (LinkerOutputDir.empty()) { - auto N = normalizePath(sys::path::parent_path(LinkerOutputFile), Saver); - if (!N) - return N.takeError(); - LinkerOutputDir = *N; + if (Error Err = checkCacheHit(J)) + return Err; + if (!J.Cached) { + TimeTraceScope JobScope("Emit individual index for DTLTO", + J.SummaryIndexPath); + if (Error Err = save(SummaryIndexFiles[Task], J.SummaryIndexPath)) + return Err; } + if (OnWriteCb) ---------------- teresajohnson wrote:
Maybe OnIndexWriteCb? https://github.com/llvm/llvm-project/pull/192629 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
