LTO: Simplify caching interface.

The NativeObjectOutput class has a design problem: it mixes up the caching
policy with the interface for output streams, which makes the client-side
code hard to follow and would for example make it harder to replace the
cache implementation in an arbitrary client.

This change separates the two aspects by moving the caching policy
to a separate field in Config, replacing NativeObjectOutput with a
NativeObjectStream class which only deals with streams and does not need to
be overridden by most clients and introducing an AddFile callback for adding
files (e.g. from the cache) to the link.

Differential Revision: https://reviews.llvm.org/D24622

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282299 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Peter Collingbourne 2016-09-23 21:33:43 +00:00
parent dc7e3f0651
commit 91d99c6edf
9 changed files with 204 additions and 301 deletions

View File

@ -7,92 +7,29 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// //
// This file defines the lto::CacheObjectOutput data structure, which allows // This file defines the localCache function, which allows clients to add a
// clients to add a filesystem cache to ThinLTO // filesystem cache to ThinLTO.
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#ifndef LLVM_LTO_CACHING_H #ifndef LLVM_LTO_CACHING_H
#define LLVM_LTO_CACHING_H #define LLVM_LTO_CACHING_H
#include "llvm/ADT/SmallString.h" #include "llvm/LTO/LTO.h"
#include "llvm/LTO/Config.h" #include <string>
#include "llvm/Support/MemoryBuffer.h"
namespace llvm { namespace llvm {
namespace lto { namespace lto {
/// Type for client-supplied callback when a buffer is loaded from the cache.
typedef std::function<void(std::string)> AddBufferFn;
/// Manage caching on the filesystem. /// This type defines the callback to add a pre-existing native object file
/// (e.g. in a cache).
/// ///
/// The general scheme is the following: /// File callbacks must be thread safe.
/// typedef std::function<void(unsigned Task, StringRef Path)> AddFileFn;
/// void do_stuff(AddBufferFn CallBack) {
/// /* ... */
/// {
/// /* Create the CacheObjectOutput pointing to a cache directory */
/// auto Output = CacheObjectOutput("/tmp/cache", CallBack)
///
/// /* Call some processing function */
/// process(Output);
///
/// } /* Callback is only called now, on destruction of the Output object */
/// /* ... */
/// }
///
///
/// void process(NativeObjectOutput &Output) {
/// /* check if caching is supported */
/// if (Output.isCachingEnabled()) {
/// auto Key = ComputeKeyForEntry(...); // "expensive" call
/// if (Output.tryLoadFromCache())
/// return; // Cache hit
/// }
///
/// auto OS = Output.getStream();
///
/// OS << ...;
/// /* Note that the callback is not called here, but only when the caller
/// destroys Output */
/// }
///
class CacheObjectOutput : public NativeObjectOutput {
/// Path to the on-disk cache directory
StringRef CacheDirectoryPath;
/// Path to this entry in the cache, initialized by tryLoadFromCache().
SmallString<128> EntryPath;
/// Path to temporary file used to buffer output that will be committed to the
/// cache entry when this object is destroyed
SmallString<128> TempFilename;
/// User-supplied callback, used to provide path to cache entry
/// (potentially after creating it).
AddBufferFn AddBuffer;
public: /// Create a local file system cache which uses the given cache directory and
/// The destructor pulls the entry from the cache and calls the AddBuffer /// file callback.
/// callback, after committing the entry into the cache on miss. NativeObjectCache localCache(std::string CacheDirectoryPath, AddFileFn AddFile);
~CacheObjectOutput();
/// Create a CacheObjectOutput: the client is supposed to create it in the
/// callback supplied to LTO::run. The \p CacheDirectoryPath points to the
/// directory on disk where to store the cache, and \p AddBuffer will be
/// called when the buffer is ready to be pulled out of the cache
/// (potentially after creating it).
CacheObjectOutput(StringRef CacheDirectoryPath, AddBufferFn AddBuffer)
: CacheDirectoryPath(CacheDirectoryPath), AddBuffer(AddBuffer) {}
/// Return an allocated stream for the output, or null in case of failure.
std::unique_ptr<raw_pwrite_stream> getStream() override;
/// Set EntryPath, try loading from a possible cache first, return true on
/// cache hit.
bool tryLoadFromCache(StringRef Key) override;
/// Returns true to signal that this implementation of NativeObjectFile
/// support caching.
bool isCachingEnabled() const override { return true; }
};
} // namespace lto } // namespace lto
} // namespace llvm } // namespace llvm

View File

@ -30,38 +30,6 @@ class raw_pwrite_stream;
namespace lto { namespace lto {
/// Abstract class representing a single Task output to be implemented by the
/// client of the LTO API.
///
/// The general scheme the API is called is the following:
///
/// void process(NativeObjectOutput &Output) {
/// /* check if caching is supported */
/// if (Output.isCachingEnabled()) {
/// auto Key = ComputeKeyForEntry(...); // "expensive" call
/// if (Output.tryLoadFromCache())
/// return; // Cache hit
/// }
///
/// auto OS = Output.getStream();
///
/// OS << ....;
/// }
///
class NativeObjectOutput {
public:
// Return an allocated stream for the output, or null in case of failure.
virtual std::unique_ptr<raw_pwrite_stream> getStream() = 0;
// Try loading from a possible cache first, return true on cache hit.
virtual bool tryLoadFromCache(StringRef Key) { return false; }
// Returns true if a cache is available
virtual bool isCachingEnabled() const { return false; }
virtual ~NativeObjectOutput() = default;
};
/// LTO configuration. A linker can configure LTO by setting fields in this data /// LTO configuration. A linker can configure LTO by setting fields in this data
/// structure and passing it to the lto::LTO constructor. /// structure and passing it to the lto::LTO constructor.
struct Config { struct Config {
@ -235,13 +203,6 @@ struct Config {
bool UseInputModulePath = false); bool UseInputModulePath = false);
}; };
/// This type defines the callback to add a native object that is generated on
/// the fly.
///
/// Output callbacks must be thread safe.
typedef std::function<std::unique_ptr<NativeObjectOutput>(unsigned Task)>
AddOutputFn;
/// A derived class of LLVMContext that initializes itself according to a given /// A derived class of LLVMContext that initializes itself according to a given
/// Config object. The purpose of this class is to tie ownership of the /// Config object. The purpose of this class is to tie ownership of the
/// diagnostic handler to the context, as opposed to the Config object (which /// diagnostic handler to the context, as opposed to the Config object (which

View File

@ -247,13 +247,47 @@ public:
} }
}; };
/// This class wraps an output stream for a native object. Most clients should
/// just be able to return an instance of this base class from the stream
/// callback, but if a client needs to perform some action after the stream is
/// written to, that can be done by deriving from this class and overriding the
/// destructor.
class NativeObjectStream {
public:
NativeObjectStream(std::unique_ptr<raw_pwrite_stream> OS) : OS(std::move(OS)) {}
std::unique_ptr<raw_pwrite_stream> OS;
virtual ~NativeObjectStream() = default;
};
/// This type defines the callback to add a native object that is generated on
/// the fly.
///
/// Stream callbacks must be thread safe.
typedef std::function<std::unique_ptr<NativeObjectStream>(unsigned Task)>
AddStreamFn;
/// This is the type of a native object cache. To request an item from the
/// cache, pass a unique string as the Key. For hits, the cached file will be
/// added to the link and this function will return AddStreamFn(). For misses,
/// the cache will return a stream callback which must be called at most once to
/// produce content for the stream. The native object stream produced by the
/// stream callback will add the file to the link after the stream is written
/// to.
///
/// Clients generally look like this:
///
/// if (AddStreamFn AddStream = Cache(Task, Key))
/// ProduceContent(AddStream);
typedef std::function<AddStreamFn(unsigned Task, StringRef Key)>
NativeObjectCache;
/// A ThinBackend defines what happens after the thin-link phase during ThinLTO. /// A ThinBackend defines what happens after the thin-link phase during ThinLTO.
/// The details of this type definition aren't important; clients can only /// The details of this type definition aren't important; clients can only
/// create a ThinBackend using one of the create*ThinBackend() functions below. /// create a ThinBackend using one of the create*ThinBackend() functions below.
typedef std::function<std::unique_ptr<ThinBackendProc>( typedef std::function<std::unique_ptr<ThinBackendProc>(
Config &C, ModuleSummaryIndex &CombinedIndex, Config &C, ModuleSummaryIndex &CombinedIndex,
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries, StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddOutputFn AddOutput)> AddStreamFn AddStream, NativeObjectCache Cache)>
ThinBackend; ThinBackend;
/// This ThinBackend runs the individual backend jobs in-process. /// This ThinBackend runs the individual backend jobs in-process.
@ -286,8 +320,9 @@ ThinBackend createWriteIndexesThinBackend(std::string OldPrefix,
/// and pass it and an array of symbol resolutions to the add() function. /// and pass it and an array of symbol resolutions to the add() function.
/// - Call the getMaxTasks() function to get an upper bound on the number of /// - Call the getMaxTasks() function to get an upper bound on the number of
/// native object files that LTO may add to the link. /// native object files that LTO may add to the link.
/// - Call the run() function. This function will use the supplied AddOutput /// - Call the run() function. This function will use the supplied AddStream
/// function to add up to getMaxTasks() native object files to the link. /// and Cache functions to add up to getMaxTasks() native object files to
/// the link.
class LTO { class LTO {
friend InputFile; friend InputFile;
@ -310,9 +345,15 @@ public:
/// full description of tasks see LTOBackend.h. /// full description of tasks see LTOBackend.h.
unsigned getMaxTasks() const; unsigned getMaxTasks() const;
/// Runs the LTO pipeline. This function calls the supplied AddOutput function /// Runs the LTO pipeline. This function calls the supplied AddStream
/// to add native object files to the link. /// function to add native object files to the link.
Error run(AddOutputFn AddOutput); ///
/// The Cache parameter is optional. If supplied, it will be used to cache
/// native object files and add them to the link.
///
/// The client will receive at most one callback (via either AddStream or
/// Cache) for each task identifier.
Error run(AddStreamFn AddStream, NativeObjectCache Cache = nullptr);
private: private:
Config Conf; Config Conf;
@ -393,8 +434,9 @@ private:
Error addThinLTO(std::unique_ptr<InputFile> Input, Error addThinLTO(std::unique_ptr<InputFile> Input,
ArrayRef<SymbolResolution> Res); ArrayRef<SymbolResolution> Res);
Error runRegularLTO(AddOutputFn AddOutput); Error runRegularLTO(AddStreamFn AddStream);
Error runThinLTO(AddOutputFn AddOutput, bool HasRegularLTO); Error runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
bool HasRegularLTO);
mutable bool CalledGetMaxTasks = false; mutable bool CalledGetMaxTasks = false;
}; };

View File

@ -20,7 +20,7 @@
#include "llvm/ADT/MapVector.h" #include "llvm/ADT/MapVector.h"
#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/LTO/Config.h" #include "llvm/LTO/LTO.h"
#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MemoryBuffer.h"
#include "llvm/Target/TargetOptions.h" #include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/IPO/FunctionImport.h" #include "llvm/Transforms/IPO/FunctionImport.h"
@ -34,12 +34,12 @@ class Target;
namespace lto { namespace lto {
/// Runs a regular LTO backend. /// Runs a regular LTO backend.
Error backend(Config &C, AddOutputFn AddStream, Error backend(Config &C, AddStreamFn AddStream,
unsigned ParallelCodeGenParallelismLevel, unsigned ParallelCodeGenParallelismLevel,
std::unique_ptr<Module> M); std::unique_ptr<Module> M);
/// Runs a ThinLTO backend. /// Runs a ThinLTO backend.
Error thinBackend(Config &C, unsigned Task, AddOutputFn AddStream, Module &M, Error thinBackend(Config &C, unsigned Task, AddStreamFn AddStream, Module &M,
ModuleSummaryIndex &CombinedIndex, ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList, const FunctionImporter::ImportMapTy &ImportList,
const GVSummaryMapTy &DefinedGlobals, const GVSummaryMapTy &DefinedGlobals,

View File

@ -12,13 +12,9 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "llvm/LTO/Caching.h" #include "llvm/LTO/Caching.h"
#ifdef HAVE_LLVM_REVISION
#include "LLVMLTORevision.h"
#endif
#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringExtras.h"
#include "llvm/Support/FileSystem.h" #include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h" #include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
@ -30,6 +26,8 @@ static void commitEntry(StringRef TempFilename, StringRef EntryPath) {
auto EC = sys::fs::rename(TempFilename, EntryPath); auto EC = sys::fs::rename(TempFilename, EntryPath);
if (EC) { if (EC) {
// Renaming failed, probably not the same filesystem, copy and delete. // Renaming failed, probably not the same filesystem, copy and delete.
// FIXME: Avoid needing to do this by creating the temporary file in the
// cache directory.
{ {
auto ReloadedBufferOrErr = MemoryBuffer::getFile(TempFilename); auto ReloadedBufferOrErr = MemoryBuffer::getFile(TempFilename);
if (auto EC = ReloadedBufferOrErr.getError()) if (auto EC = ReloadedBufferOrErr.getError())
@ -48,51 +46,54 @@ static void commitEntry(StringRef TempFilename, StringRef EntryPath) {
} }
} }
CacheObjectOutput::~CacheObjectOutput() { NativeObjectCache lto::localCache(std::string CacheDirectoryPath,
if (EntryPath.empty()) AddFileFn AddFile) {
// The entry was never used by the client (tryLoadFromCache() wasn't called) return [=](unsigned Task, StringRef Key) -> AddStreamFn {
return; // First, see if we have a cache hit.
// TempFilename is only set if getStream() was called, i.e. on cache miss when SmallString<64> EntryPath;
// tryLoadFromCache() returned false. And EntryPath is valid if a Key was sys::path::append(EntryPath, CacheDirectoryPath, Key);
// submitted, otherwise it has been set to CacheDirectoryPath in if (sys::fs::exists(EntryPath)) {
// tryLoadFromCache. AddFile(Task, EntryPath);
if (!TempFilename.empty()) { return AddStreamFn();
if (EntryPath == CacheDirectoryPath) }
// The Key supplied to tryLoadFromCache was empty, do not commit the temp.
EntryPath = TempFilename;
else
// We commit the tempfile into the cache now, by moving it to EntryPath.
commitEntry(TempFilename, EntryPath);
}
// Supply the cache path to the user.
AddBuffer(EntryPath.str());
}
// Return an allocated stream for the output, or null in case of failure. // This native object stream is responsible for commiting the resulting
std::unique_ptr<raw_pwrite_stream> CacheObjectOutput::getStream() { // file to the cache and calling AddFile to add it to the link.
assert(!EntryPath.empty() && "API Violation: client didn't call " struct CacheStream : NativeObjectStream {
"tryLoadFromCache() before getStream()"); AddFileFn AddFile;
// Write to a temporary to avoid race condition std::string TempFilename;
int TempFD; std::string EntryPath;
std::error_code EC = unsigned Task;
sys::fs::createTemporaryFile("Thin", "tmp.o", TempFD, TempFilename);
if (EC) {
errs() << "Error: " << EC.message() << "\n";
report_fatal_error("ThinLTO: Can't get a temporary file");
}
return llvm::make_unique<raw_fd_ostream>(TempFD, /* ShouldClose */ true);
}
// Try loading from a possible cache first, return true on cache hit. CacheStream(std::unique_ptr<raw_pwrite_stream> OS, AddFileFn AddFile,
bool CacheObjectOutput::tryLoadFromCache(StringRef Key) { std::string TempFilename, std::string EntryPath,
assert(!CacheDirectoryPath.empty() && unsigned Task)
"CacheObjectOutput was initialized without a cache path"); : NativeObjectStream(std::move(OS)), AddFile(AddFile),
if (Key.empty()) { TempFilename(TempFilename), EntryPath(EntryPath), Task(Task) {}
// Client didn't compute a valid key. EntryPath has been set to
// CacheDirectoryPath. ~CacheStream() {
EntryPath = CacheDirectoryPath; // Make sure the file is closed before committing it.
return false; OS.reset();
} commitEntry(TempFilename, EntryPath);
sys::path::append(EntryPath, CacheDirectoryPath, Key); AddFile(Task, EntryPath);
return sys::fs::exists(EntryPath); }
};
return [=](size_t Task) -> std::unique_ptr<NativeObjectStream> {
// Write to a temporary to avoid race condition
int TempFD;
SmallString<64> TempFilename;
std::error_code EC =
sys::fs::createTemporaryFile("Thin", "tmp.o", TempFD, TempFilename);
if (EC) {
errs() << "Error: " << EC.message() << "\n";
report_fatal_error("ThinLTO: Can't get a temporary file");
}
// This CacheStream will move the temporary file into the cache when done.
return make_unique<CacheStream>(
llvm::make_unique<raw_fd_ostream>(TempFD, /* ShouldClose */ true),
AddFile, TempFilename.str(), EntryPath.str(), Task);
};
};
} }

View File

@ -409,19 +409,19 @@ unsigned LTO::getMaxTasks() const {
return RegularLTO.ParallelCodeGenParallelismLevel + ThinLTO.ModuleMap.size(); return RegularLTO.ParallelCodeGenParallelismLevel + ThinLTO.ModuleMap.size();
} }
Error LTO::run(AddOutputFn AddOutput) { Error LTO::run(AddStreamFn AddStream, NativeObjectCache Cache) {
// Save the status of having a regularLTO combined module, as // Save the status of having a regularLTO combined module, as
// this is needed for generating the ThinLTO Task ID, and // this is needed for generating the ThinLTO Task ID, and
// the CombinedModule will be moved at the end of runRegularLTO. // the CombinedModule will be moved at the end of runRegularLTO.
bool HasRegularLTO = RegularLTO.CombinedModule != nullptr; bool HasRegularLTO = RegularLTO.CombinedModule != nullptr;
// Invoke regular LTO if there was a regular LTO module to start with. // Invoke regular LTO if there was a regular LTO module to start with.
if (HasRegularLTO) if (HasRegularLTO)
if (auto E = runRegularLTO(AddOutput)) if (auto E = runRegularLTO(AddStream))
return E; return E;
return runThinLTO(AddOutput, HasRegularLTO); return runThinLTO(AddStream, Cache, HasRegularLTO);
} }
Error LTO::runRegularLTO(AddOutputFn AddOutput) { Error LTO::runRegularLTO(AddStreamFn AddStream) {
// Make sure commons have the right size/alignment: we kept the largest from // Make sure commons have the right size/alignment: we kept the largest from
// all the prevailing when adding the inputs, and we apply it here. // all the prevailing when adding the inputs, and we apply it here.
const DataLayout &DL = RegularLTO.CombinedModule->getDataLayout(); const DataLayout &DL = RegularLTO.CombinedModule->getDataLayout();
@ -478,7 +478,7 @@ Error LTO::runRegularLTO(AddOutputFn AddOutput) {
!Conf.PostInternalizeModuleHook(0, *RegularLTO.CombinedModule)) !Conf.PostInternalizeModuleHook(0, *RegularLTO.CombinedModule))
return Error(); return Error();
} }
return backend(Conf, AddOutput, RegularLTO.ParallelCodeGenParallelismLevel, return backend(Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel,
std::move(RegularLTO.CombinedModule)); std::move(RegularLTO.CombinedModule));
} }
@ -507,7 +507,8 @@ public:
class InProcessThinBackend : public ThinBackendProc { class InProcessThinBackend : public ThinBackendProc {
ThreadPool BackendThreadPool; ThreadPool BackendThreadPool;
AddOutputFn AddOutput; AddStreamFn AddStream;
NativeObjectCache Cache;
Optional<Error> Err; Optional<Error> Err;
std::mutex ErrMu; std::mutex ErrMu;
@ -517,42 +518,40 @@ public:
Config &Conf, ModuleSummaryIndex &CombinedIndex, Config &Conf, ModuleSummaryIndex &CombinedIndex,
unsigned ThinLTOParallelismLevel, unsigned ThinLTOParallelismLevel,
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries, const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddOutputFn AddOutput) AddStreamFn AddStream, NativeObjectCache Cache)
: ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries), : ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries),
BackendThreadPool(ThinLTOParallelismLevel), BackendThreadPool(ThinLTOParallelismLevel),
AddOutput(std::move(AddOutput)) {} AddStream(std::move(AddStream)), Cache(std::move(Cache)) {}
Error runThinLTOBackendThread( Error runThinLTOBackendThread(
AddOutputFn AddOutput, unsigned Task, MemoryBufferRef MBRef, AddStreamFn AddStream, NativeObjectCache Cache, unsigned Task,
ModuleSummaryIndex &CombinedIndex, MemoryBufferRef MBRef, ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList, const FunctionImporter::ImportMapTy &ImportList,
const FunctionImporter::ExportSetTy &ExportList, const FunctionImporter::ExportSetTy &ExportList,
const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR, const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
const GVSummaryMapTy &DefinedGlobals, const GVSummaryMapTy &DefinedGlobals,
MapVector<StringRef, MemoryBufferRef> &ModuleMap) { MapVector<StringRef, MemoryBufferRef> &ModuleMap) {
auto RunThinBackend = [&](AddStreamFn AddStream) {
LTOLLVMContext BackendContext(Conf);
ErrorOr<std::unique_ptr<Module>> MOrErr =
parseBitcodeFile(MBRef, BackendContext);
assert(MOrErr && "Unable to load module in thread?");
auto ModuleIdentifier = MBRef.getBufferIdentifier(); return thinBackend(Conf, Task, AddStream, **MOrErr, CombinedIndex,
auto Output = AddOutput(Task); ImportList, DefinedGlobals, ModuleMap);
if (Output->isCachingEnabled()) {
SmallString<40> Key;
// The module may be cached, this helps handling it.
computeCacheKey(Key, CombinedIndex, ModuleIdentifier, ImportList,
ExportList, ResolvedODR, DefinedGlobals);
if (Output->tryLoadFromCache(Key))
return Error();
}
LTOLLVMContext BackendContext(Conf);
ErrorOr<std::unique_ptr<Module>> MOrErr =
parseBitcodeFile(MBRef, BackendContext);
assert(MOrErr && "Unable to load module in thread?");
auto AddOutputWrapper = [&](unsigned TaskId) {
assert(Task == TaskId && "Unexpexted TaskId mismatch");
return std::move(Output);
}; };
return thinBackend(Conf, Task, AddOutputWrapper, **MOrErr, CombinedIndex,
ImportList, DefinedGlobals, ModuleMap); if (!Cache)
return RunThinBackend(AddStream);
SmallString<40> Key;
// The module may be cached, this helps handling it.
computeCacheKey(Key, CombinedIndex, MBRef.getBufferIdentifier(),
ImportList, ExportList, ResolvedODR, DefinedGlobals);
if (AddStreamFn CacheAddStream = Cache(Task, Key))
return RunThinBackend(CacheAddStream);
return Error();
} }
Error start( Error start(
@ -574,8 +573,8 @@ public:
const GVSummaryMapTy &DefinedGlobals, const GVSummaryMapTy &DefinedGlobals,
MapVector<StringRef, MemoryBufferRef> &ModuleMap) { MapVector<StringRef, MemoryBufferRef> &ModuleMap) {
Error E = runThinLTOBackendThread( Error E = runThinLTOBackendThread(
AddOutput, Task, MBRef, CombinedIndex, ImportList, ExportList, AddStream, Cache, Task, MBRef, CombinedIndex, ImportList,
ResolvedODR, DefinedGlobals, ModuleMap); ExportList, ResolvedODR, DefinedGlobals, ModuleMap);
if (E) { if (E) {
std::unique_lock<std::mutex> L(ErrMu); std::unique_lock<std::mutex> L(ErrMu);
if (Err) if (Err)
@ -602,10 +601,10 @@ public:
ThinBackend lto::createInProcessThinBackend(unsigned ParallelismLevel) { ThinBackend lto::createInProcessThinBackend(unsigned ParallelismLevel) {
return [=](Config &Conf, ModuleSummaryIndex &CombinedIndex, return [=](Config &Conf, ModuleSummaryIndex &CombinedIndex,
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries, const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddOutputFn AddOutput) { AddStreamFn AddStream, NativeObjectCache Cache) {
return llvm::make_unique<InProcessThinBackend>( return llvm::make_unique<InProcessThinBackend>(
Conf, CombinedIndex, ParallelismLevel, ModuleToDefinedGVSummaries, Conf, CombinedIndex, ParallelismLevel, ModuleToDefinedGVSummaries,
AddOutput); AddStream, Cache);
}; };
} }
@ -693,14 +692,15 @@ ThinBackend lto::createWriteIndexesThinBackend(std::string OldPrefix,
std::string LinkedObjectsFile) { std::string LinkedObjectsFile) {
return [=](Config &Conf, ModuleSummaryIndex &CombinedIndex, return [=](Config &Conf, ModuleSummaryIndex &CombinedIndex,
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries, const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddOutputFn AddOutput) { AddStreamFn AddStream, NativeObjectCache Cache) {
return llvm::make_unique<WriteIndexesThinBackend>( return llvm::make_unique<WriteIndexesThinBackend>(
Conf, CombinedIndex, ModuleToDefinedGVSummaries, OldPrefix, NewPrefix, Conf, CombinedIndex, ModuleToDefinedGVSummaries, OldPrefix, NewPrefix,
ShouldEmitImportsFiles, LinkedObjectsFile); ShouldEmitImportsFiles, LinkedObjectsFile);
}; };
} }
Error LTO::runThinLTO(AddOutputFn AddOutput, bool HasRegularLTO) { Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
bool HasRegularLTO) {
if (ThinLTO.ModuleMap.empty()) if (ThinLTO.ModuleMap.empty())
return Error(); return Error();
@ -759,8 +759,9 @@ Error LTO::runThinLTO(AddOutputFn AddOutput, bool HasRegularLTO) {
thinLTOResolveWeakForLinkerInIndex(ThinLTO.CombinedIndex, isPrevailing, thinLTOResolveWeakForLinkerInIndex(ThinLTO.CombinedIndex, isPrevailing,
recordNewLinkage); recordNewLinkage);
std::unique_ptr<ThinBackendProc> BackendProc = ThinLTO.Backend( std::unique_ptr<ThinBackendProc> BackendProc =
Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, AddOutput); ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
AddStream, Cache);
// Partition numbers for ThinLTO jobs start at 1 (see comments for // Partition numbers for ThinLTO jobs start at 1 (see comments for
// GlobalResolution in LTO.h). Task numbers, however, start at // GlobalResolution in LTO.h). Task numbers, however, start at

View File

@ -199,34 +199,20 @@ bool opt(Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod,
return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod); return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod);
} }
/// Monolithic LTO does not support caching (yet), this is a convenient wrapper void codegen(Config &Conf, TargetMachine *TM, AddStreamFn AddStream,
/// around AddOutput to workaround this.
static AddOutputFn getUncachedOutputWrapper(AddOutputFn &AddOutput,
unsigned Task) {
return [Task, &AddOutput](unsigned TaskId) {
auto Output = AddOutput(Task);
if (Output->isCachingEnabled() && Output->tryLoadFromCache(""))
report_fatal_error("Cache hit without a valid key?");
assert(Task == TaskId && "Unexpexted TaskId mismatch");
return Output;
};
}
void codegen(Config &Conf, TargetMachine *TM, AddOutputFn AddOutput,
unsigned Task, Module &Mod) { unsigned Task, Module &Mod) {
if (Conf.PreCodeGenModuleHook && !Conf.PreCodeGenModuleHook(Task, Mod)) if (Conf.PreCodeGenModuleHook && !Conf.PreCodeGenModuleHook(Task, Mod))
return; return;
auto Output = AddOutput(Task); auto Stream = AddStream(Task);
std::unique_ptr<raw_pwrite_stream> OS = Output->getStream();
legacy::PassManager CodeGenPasses; legacy::PassManager CodeGenPasses;
if (TM->addPassesToEmitFile(CodeGenPasses, *OS, if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS,
TargetMachine::CGFT_ObjectFile)) TargetMachine::CGFT_ObjectFile))
report_fatal_error("Failed to setup codegen"); report_fatal_error("Failed to setup codegen");
CodeGenPasses.run(Mod); CodeGenPasses.run(Mod);
} }
void splitCodeGen(Config &C, TargetMachine *TM, AddOutputFn AddOutput, void splitCodeGen(Config &C, TargetMachine *TM, AddStreamFn AddStream,
unsigned ParallelCodeGenParallelismLevel, unsigned ParallelCodeGenParallelismLevel,
std::unique_ptr<Module> Mod) { std::unique_ptr<Module> Mod) {
ThreadPool CodegenThreadPool(ParallelCodeGenParallelismLevel); ThreadPool CodegenThreadPool(ParallelCodeGenParallelismLevel);
@ -260,9 +246,7 @@ void splitCodeGen(Config &C, TargetMachine *TM, AddOutputFn AddOutput,
std::unique_ptr<TargetMachine> TM = std::unique_ptr<TargetMachine> TM =
createTargetMachine(C, MPartInCtx->getTargetTriple(), T); createTargetMachine(C, MPartInCtx->getTargetTriple(), T);
codegen(C, TM.get(), codegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx);
getUncachedOutputWrapper(AddOutput, ThreadId), ThreadId,
*MPartInCtx);
}, },
// Pass BC using std::move to ensure that it get moved rather than // Pass BC using std::move to ensure that it get moved rather than
// copied into the thread's context. // copied into the thread's context.
@ -299,7 +283,7 @@ static void handleAsmUndefinedRefs(Module &Mod, TargetMachine &TM) {
updateCompilerUsed(Mod, TM, AsmUndefinedRefs); updateCompilerUsed(Mod, TM, AsmUndefinedRefs);
} }
Error lto::backend(Config &C, AddOutputFn AddOutput, Error lto::backend(Config &C, AddStreamFn AddStream,
unsigned ParallelCodeGenParallelismLevel, unsigned ParallelCodeGenParallelismLevel,
std::unique_ptr<Module> Mod) { std::unique_ptr<Module> Mod) {
Expected<const Target *> TOrErr = initAndLookupTarget(C, *Mod); Expected<const Target *> TOrErr = initAndLookupTarget(C, *Mod);
@ -316,15 +300,15 @@ Error lto::backend(Config &C, AddOutputFn AddOutput,
return Error(); return Error();
if (ParallelCodeGenParallelismLevel == 1) { if (ParallelCodeGenParallelismLevel == 1) {
codegen(C, TM.get(), getUncachedOutputWrapper(AddOutput, 0), 0, *Mod); codegen(C, TM.get(), AddStream, 0, *Mod);
} else { } else {
splitCodeGen(C, TM.get(), AddOutput, ParallelCodeGenParallelismLevel, splitCodeGen(C, TM.get(), AddStream, ParallelCodeGenParallelismLevel,
std::move(Mod)); std::move(Mod));
} }
return Error(); return Error();
} }
Error lto::thinBackend(Config &Conf, unsigned Task, AddOutputFn AddOutput, Error lto::thinBackend(Config &Conf, unsigned Task, AddStreamFn AddStream,
Module &Mod, ModuleSummaryIndex &CombinedIndex, Module &Mod, ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList, const FunctionImporter::ImportMapTy &ImportList,
const GVSummaryMapTy &DefinedGlobals, const GVSummaryMapTy &DefinedGlobals,
@ -339,7 +323,7 @@ Error lto::thinBackend(Config &Conf, unsigned Task, AddOutputFn AddOutput,
handleAsmUndefinedRefs(Mod, *TM); handleAsmUndefinedRefs(Mod, *TM);
if (Conf.CodeGenOnly) { if (Conf.CodeGenOnly) {
codegen(Conf, TM.get(), AddOutput, Task, Mod); codegen(Conf, TM.get(), AddStream, Task, Mod);
return Error(); return Error();
} }
@ -379,6 +363,6 @@ Error lto::thinBackend(Config &Conf, unsigned Task, AddOutputFn AddOutput,
if (!opt(Conf, TM.get(), Task, Mod, /*IsThinLto=*/true)) if (!opt(Conf, TM.get(), Task, Mod, /*IsThinLto=*/true))
return Error(); return Error();
codegen(Conf, TM.get(), AddOutput, Task, Mod); codegen(Conf, TM.get(), AddStream, Task, Mod);
return Error(); return Error();
} }

View File

@ -675,24 +675,6 @@ static void getThinLTOOldAndNewPrefix(std::string &OldPrefix,
NewPrefix = Split.second.str(); NewPrefix = Split.second.str();
} }
namespace {
// Define the LTOOutput handling
class LTOOutput : public lto::NativeObjectOutput {
StringRef Path;
public:
LTOOutput(StringRef Path) : Path(Path) {}
// Open the filename \p Path and allocate a stream.
std::unique_ptr<raw_pwrite_stream> getStream() override {
int FD;
std::error_code EC = sys::fs::openFileForWrite(Path, FD, sys::fs::F_None);
if (EC)
message(LDPL_FATAL, "Could not open file: %s", EC.message().c_str());
return llvm::make_unique<llvm::raw_fd_ostream>(FD, true);
}
};
}
static std::unique_ptr<LTO> createLTO() { static std::unique_ptr<LTO> createLTO() {
Config Conf; Config Conf;
ThinBackend Backend; ThinBackend Backend;
@ -831,21 +813,27 @@ static ld_plugin_status allSymbolsReadHook() {
std::vector<uintptr_t> IsTemporary(MaxTasks); std::vector<uintptr_t> IsTemporary(MaxTasks);
std::vector<SmallString<128>> Filenames(MaxTasks); std::vector<SmallString<128>> Filenames(MaxTasks);
auto AddOutput = auto AddStream =
[&](size_t Task) -> std::unique_ptr<lto::NativeObjectOutput> { [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> {
auto &OutputName = Filenames[Task]; IsTemporary[Task] = !SaveTemps;
getOutputFileName(Filename, /*TempOutFile=*/!SaveTemps, OutputName, getOutputFileName(Filename, /*TempOutFile=*/!SaveTemps, Filenames[Task],
MaxTasks > 1 ? Task : -1); MaxTasks > 1 ? Task : -1);
IsTemporary[Task] = !SaveTemps && options::cache_dir.empty(); int FD;
if (options::cache_dir.empty()) std::error_code EC =
return llvm::make_unique<LTOOutput>(OutputName); sys::fs::openFileForWrite(Filenames[Task], FD, sys::fs::F_None);
if (EC)
return llvm::make_unique<CacheObjectOutput>( message(LDPL_FATAL, "Could not open file: %s", EC.message().c_str());
options::cache_dir, return llvm::make_unique<lto::NativeObjectStream>(
[&OutputName](std::string EntryPath) { OutputName = EntryPath; }); llvm::make_unique<llvm::raw_fd_ostream>(FD, true));
}; };
check(Lto->run(AddOutput)); auto AddFile = [&](size_t Task, StringRef Path) { Filenames[Task] = Path; };
NativeObjectCache Cache;
if (!options::cache_dir.empty())
Cache = localCache(options::cache_dir, AddFile);
check(Lto->run(AddStream, Cache));
if (options::TheOutputType == options::OT_DISABLE || if (options::TheOutputType == options::OT_DISABLE ||
options::TheOutputType == options::OT_BC_ONLY) options::TheOutputType == options::OT_BC_ONLY)

View File

@ -95,22 +95,6 @@ template <typename T> static T check(ErrorOr<T> E, std::string Msg) {
return T(); return T();
} }
namespace {
// Define the LTOOutput handling
class LTOOutput : public lto::NativeObjectOutput {
std::string Path;
public:
LTOOutput(std::string Path) : Path(std::move(Path)) {}
std::unique_ptr<raw_pwrite_stream> getStream() override {
std::error_code EC;
auto S = llvm::make_unique<raw_fd_ostream>(Path, EC, sys::fs::F_None);
check(EC, Path);
return std::move(S);
}
};
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
InitializeAllTargets(); InitializeAllTargets();
InitializeAllTargetMCs(); InitializeAllTargetMCs();
@ -203,23 +187,28 @@ int main(int argc, char **argv) {
if (HasErrors) if (HasErrors)
return 1; return 1;
auto AddOutput = auto AddStream =
[&](size_t Task) -> std::unique_ptr<lto::NativeObjectOutput> { [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> {
std::string Path = OutputFilename + "." + utostr(Task); std::string Path = OutputFilename + "." + utostr(Task);
if (CacheDir.empty())
return llvm::make_unique<LTOOutput>(std::move(Path));
return llvm::make_unique<CacheObjectOutput>( std::error_code EC;
CacheDir, [Path](std::string EntryPath) { auto S = llvm::make_unique<raw_fd_ostream>(Path, EC, sys::fs::F_None);
// Load the entry from the cache now. check(EC, Path);
auto ReloadedBufferOrErr = MemoryBuffer::getFile(EntryPath); return llvm::make_unique<lto::NativeObjectStream>(std::move(S));
if (auto EC = ReloadedBufferOrErr.getError())
report_fatal_error(Twine("Can't reload cached file '") + EntryPath +
"': " + EC.message() + "\n");
*LTOOutput(Path).getStream() << (*ReloadedBufferOrErr)->getBuffer();
});
}; };
check(Lto.run(AddOutput), "LTO::run failed"); auto AddFile = [&](size_t Task, StringRef Path) {
auto ReloadedBufferOrErr = MemoryBuffer::getFile(Path);
if (auto EC = ReloadedBufferOrErr.getError())
report_fatal_error(Twine("Can't reload cached file '") + Path + "': " +
EC.message() + "\n");
*AddStream(Task)->OS << (*ReloadedBufferOrErr)->getBuffer();
};
NativeObjectCache Cache;
if (!CacheDir.empty())
Cache = localCache(CacheDir, AddFile);
check(Lto.run(AddStream, Cache), "LTO::run failed");
} }