forked from llvm-mirror/llvm
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ThinLTO] Add caching to the new LTO API
Add the ability to plug a cache on the LTO API. I tried to write such that a linker implementation can control the cache backend. This is intrusive and I'm not totally happy with it, but I can't figure out a better design right now. Differential Revision: https://reviews.llvm.org/D23599 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@279576 91177308-0d34-0410-b5e6-96231b3b80d8
- Loading branch information
Showing
8 changed files
with
393 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
//===- Caching.h - LLVM Link Time Optimizer Configuration -----------------===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This file defines the lto::CacheObjectOutput data structure, which allows | ||
// clients to add a filesystem cache to ThinLTO | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_LTO_CACHING_H | ||
#define LLVM_LTO_CACHING_H | ||
|
||
#include "llvm/ADT/SmallString.h" | ||
#include "llvm/LTO/Config.h" | ||
#include "llvm/Support/MemoryBuffer.h" | ||
|
||
namespace llvm { | ||
namespace lto { | ||
/// Type for client-supplied callback when a buffer is loaded from the cache. | ||
typedef std::function<void(std::unique_ptr<MemoryBuffer>)> AddBufferFn; | ||
|
||
/// Manage caching on the filesystem. | ||
/// | ||
/// The general scheme is the following: | ||
/// | ||
/// 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, called when the buffer is pulled out of the cache | ||
/// (potentially after creating it). | ||
AddBufferFn AddBuffer; | ||
|
||
public: | ||
/// The destructor pulls the entry from the cache and calls the AddBuffer | ||
/// callback, after committing the entry into the cache on miss. | ||
~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 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 llvm | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,6 +48,7 @@ endif() | |
|
||
|
||
add_llvm_library(LLVMLTO | ||
Caching.cpp | ||
LTO.cpp | ||
LTOBackend.cpp | ||
LTOModule.cpp | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
//===-Caching.cpp - LLVM Link Time Optimizer Cache Handling ---------------===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This file implements the Caching for ThinLTO. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "llvm/LTO/Caching.h" | ||
|
||
#ifdef HAVE_LLVM_REVISION | ||
#include "LLVMLTORevision.h" | ||
#endif | ||
|
||
#include "llvm/ADT/StringExtras.h" | ||
#include "llvm/Support/FileSystem.h" | ||
#include "llvm/Support/Path.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
|
||
using namespace llvm; | ||
using namespace llvm::lto; | ||
|
||
static void commitEntry(StringRef TempFilename, StringRef EntryPath) { | ||
// Rename to final destination (hopefully race condition won't matter here) | ||
auto EC = sys::fs::rename(TempFilename, EntryPath); | ||
if (EC) { | ||
// Renaming failed, probably not the same filesystem, copy and delete. | ||
{ | ||
auto ReloadedBufferOrErr = MemoryBuffer::getFile(TempFilename); | ||
if (auto EC = ReloadedBufferOrErr.getError()) | ||
report_fatal_error(Twine("Failed to open temp file '") + TempFilename + | ||
"': " + EC.message() + "\n"); | ||
|
||
raw_fd_ostream OS(EntryPath, EC, sys::fs::F_None); | ||
if (EC) | ||
report_fatal_error(Twine("Failed to open ") + EntryPath + | ||
" to save cached entry\n"); | ||
// I'm not sure what are the guarantee if two processes are doing this | ||
// at the same time. | ||
OS << (*ReloadedBufferOrErr)->getBuffer(); | ||
} | ||
sys::fs::remove(TempFilename); | ||
} | ||
} | ||
|
||
CacheObjectOutput::~CacheObjectOutput() { | ||
if (EntryPath.empty()) | ||
// The entry was never used by the client (tryLoadFromCache() wasn't called) | ||
return; | ||
// TempFilename is only set if getStream() was called, i.e. on cache miss when | ||
// tryLoadFromCache() returned false. And EntryPath is valid if a Key was | ||
// submitted, otherwise it has been set to CacheDirectoryPath in | ||
// tryLoadFromCache. | ||
if (!TempFilename.empty()) { | ||
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); | ||
} | ||
// Load the entry from the cache now. | ||
auto ReloadedBufferOrErr = MemoryBuffer::getFile(EntryPath); | ||
if (auto EC = ReloadedBufferOrErr.getError()) | ||
report_fatal_error(Twine("Can't reload cached file '") + EntryPath + "': " + | ||
EC.message() + "\n"); | ||
|
||
// Supply the resulting buffer to the user. | ||
AddBuffer(std::move(*ReloadedBufferOrErr)); | ||
} | ||
|
||
// Return an allocated stream for the output, or null in case of failure. | ||
std::unique_ptr<raw_pwrite_stream> CacheObjectOutput::getStream() { | ||
assert(!EntryPath.empty() && "API Violation: client didn't call " | ||
"tryLoadFromCache() before getStream()"); | ||
// Write to a temporary to avoid race condition | ||
int TempFD; | ||
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"); | ||
} | ||
return llvm::make_unique<raw_fd_ostream>(TempFD, /* ShouldClose */ true); | ||
} | ||
|
||
// Try loading from a possible cache first, return true on cache hit. | ||
bool CacheObjectOutput::tryLoadFromCache(StringRef Key) { | ||
assert(!CacheDirectoryPath.empty() && | ||
"CacheObjectOutput was initialized without a cache path"); | ||
if (Key.empty()) { | ||
// Client didn't compute a valid key. EntryPath has been set to | ||
// CacheDirectoryPath. | ||
EntryPath = CacheDirectoryPath; | ||
return false; | ||
} | ||
sys::path::append(EntryPath, CacheDirectoryPath, Key); | ||
return sys::fs::exists(EntryPath); | ||
} |
Oops, something went wrong.