Skip to content
This repository was archived by the owner on Jul 3, 2021. It is now read-only.

Commit 75dff62

Browse files
Simple name-based object cache
1 parent b7212c3 commit 75dff62

File tree

7 files changed

+219
-7
lines changed

7 files changed

+219
-7
lines changed

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ add_executable(JitFromScratch
4040
main.cpp
4141
JitFromScratch.cpp
4242
SimpleOptimizer.cpp
43+
SimpleObjectCache.cpp
4344
)
4445

4546
target_include_directories(JitFromScratch PRIVATE
@@ -125,4 +126,9 @@ add_custom_target(run
125126
COMMENT "Running JitFromScratch"
126127
)
127128

129+
add_custom_target(run-cached
130+
COMMAND $<TARGET_FILE:JitFromScratch> ${debug_args} -cache-dir=cache/
131+
COMMENT "Running JitFromScratch (with object cache)"
132+
)
133+
128134
add_subdirectory(test)

JitFromScratch.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
using namespace llvm;
1616
using namespace llvm::orc;
1717

18-
JitFromScratch::JitFromScratch(ExitOnError ExitOnErr)
18+
JitFromScratch::JitFromScratch(ExitOnError ExitOnErr,
19+
const std::string &CacheDir)
1920
: ES(std::make_unique<ExecutionSession>()),
2021
TM(createTargetMachine(ExitOnErr)),
22+
ObjCache(std::make_unique<SimpleObjectCache>(CacheDir)),
2123
GDBListener(JITEventListener::createGDBRegistrationListener()),
2224
ObjLinkingLayer(*ES, createMemoryManagerFtor()),
23-
CompileLayer(*ES, ObjLinkingLayer, SimpleCompiler(*TM)),
25+
CompileLayer(*ES, ObjLinkingLayer, SimpleCompiler(*TM, ObjCache.get())),
2426
OptimizeLayer(*ES, CompileLayer) {
2527
ObjLinkingLayer.setNotifyLoaded(createNotifyLoadedFtor());
2628
if (auto R = createHostProcessResolver())
@@ -94,7 +96,22 @@ Error JitFromScratch::applyDataLayout(Module &M) {
9496

9597
Error JitFromScratch::submitModule(std::unique_ptr<Module> M,
9698
std::unique_ptr<LLVMContext> C,
97-
unsigned OptLevel) {
99+
unsigned OptLevel, bool AddToCache) {
100+
if (AddToCache)
101+
ObjCache->setCacheModuleName(*M);
102+
103+
auto Obj = ObjCache->getCachedObject(*M);
104+
if (!Obj) {
105+
M.~unique_ptr();
106+
return Obj.takeError();
107+
}
108+
109+
if (Obj->hasValue()) {
110+
M.~unique_ptr();
111+
return ObjLinkingLayer.add(ES->getMainJITDylib(),
112+
std::move(Obj->getValue()));
113+
}
114+
98115
LLVM_DEBUG(dbgs() << "Submit IR module:\n\n" << *M << "\n\n");
99116

100117
if (auto Err = applyDataLayout(*M))

JitFromScratch.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
#include <memory>
1818
#include <string>
1919

20+
#include "SimpleObjectCache.h"
21+
2022
class JitFromScratch {
2123
public:
22-
JitFromScratch(llvm::ExitOnError ExitOnErr);
24+
JitFromScratch(llvm::ExitOnError ExitOnErr,
25+
const std::string &CacheDir = "");
2326

2427
// Not a value type.
2528
JitFromScratch(const JitFromScratch &) = delete;
@@ -37,7 +40,7 @@ class JitFromScratch {
3740

3841
llvm::Error submitModule(std::unique_ptr<llvm::Module> M,
3942
std::unique_ptr<llvm::LLVMContext> C,
40-
unsigned OptLevel);
43+
unsigned OptLevel, bool AddToCache);
4144

4245
template <class Signature_t>
4346
llvm::Expected<std::function<Signature_t>> getFunction(llvm::StringRef Name) {
@@ -51,6 +54,8 @@ class JitFromScratch {
5154
private:
5255
std::unique_ptr<llvm::orc::ExecutionSession> ES;
5356
std::unique_ptr<llvm::TargetMachine> TM;
57+
58+
std::unique_ptr<SimpleObjectCache> ObjCache;
5459
llvm::JITEventListener *GDBListener;
5560

5661
llvm::orc::RTDyldObjectLinkingLayer ObjLinkingLayer;

SimpleObjectCache.cpp

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#include "SimpleObjectCache.h"
2+
3+
#include <llvm/ADT/None.h>
4+
#include <llvm/ADT/SmallString.h>
5+
#include <llvm/ADT/Twine.h>
6+
#include <llvm/IR/Module.h>
7+
#include <llvm/Support/Debug.h>
8+
#include <llvm/Support/FileSystem.h>
9+
#include <llvm/Support/MemoryBuffer.h>
10+
#include <llvm/Support/Path.h>
11+
#include <llvm/Support/raw_ostream.h>
12+
13+
#define DEBUG_TYPE "jitfromscratch"
14+
15+
using namespace llvm;
16+
17+
SimpleObjectCache::SimpleObjectCache(std::string Dir)
18+
: Enabled(!Dir.empty()), CacheDir(endWithSeparator(std::move(Dir))) {
19+
if (!sys::fs::exists(CacheDir)) {
20+
LLVM_DEBUG(dbgs() << format("Create new cache directory '%s'\n\n",
21+
CacheDir.c_str()));
22+
std::error_code EC = sys::fs::create_directories(CacheDir);
23+
if (EC) {
24+
LLVM_DEBUG(dbgs() << format(
25+
"Creating new cache directory '%s' failed with "
26+
"error code %d; Caching disabled\n\n",
27+
CacheDir.c_str(), EC.value()));
28+
Enabled = false;
29+
}
30+
}
31+
}
32+
33+
std::string SimpleObjectCache::endWithSeparator(std::string Path) {
34+
return Path.back() == '/' ? Path : Path + "/";
35+
}
36+
37+
// Implements llvm::ObjectCache::notifyObjectCompiled, called from CompileLayer
38+
void SimpleObjectCache::notifyObjectCompiled(const Module *M,
39+
MemoryBufferRef Obj) {
40+
assert(M && "Caching requires module");
41+
42+
auto R = getCacheFileName(M->getModuleIdentifier());
43+
if (!R.hasValue())
44+
return;
45+
46+
std::string F = std::move(R.getValue());
47+
if (auto EC = sys::fs::create_directories(sys::path::parent_path(F))) {
48+
LLVM_DEBUG(dbgs() << format(
49+
"Writing cached object '%s' failed with error code %d\n\n",
50+
F.c_str(), EC.value()));
51+
return;
52+
}
53+
54+
std::error_code EC;
55+
raw_fd_ostream OS(F, EC, sys::fs::F_None);
56+
if (EC) {
57+
LLVM_DEBUG(dbgs() << format(
58+
"Writing cached object '%s' failed with error code %d\n\n",
59+
F.c_str(), EC.value()));
60+
return;
61+
}
62+
63+
LLVM_DEBUG(dbgs() << format("Write cached object '%s'\n\n", F.c_str()));
64+
65+
OS.write(Obj.getBufferStart(), Obj.getBufferSize());
66+
OS.close();
67+
}
68+
69+
// Implements llvm::ObjectCache::getObject, called from CompileLayer
70+
std::unique_ptr<MemoryBuffer> SimpleObjectCache::getObject(const Module *M) {
71+
assert(M && "Lookup requires module");
72+
73+
auto R = getCachedObject(*M);
74+
if (!R) {
75+
logAllUnhandledErrors(R.takeError(), dbgs(), "SimpleObjectCache: ");
76+
return nullptr; // Error
77+
}
78+
79+
if (!R->hasValue())
80+
return nullptr; // No cache entry
81+
82+
return std::forward<std::unique_ptr<MemoryBuffer>>(R->getValue());
83+
}
84+
85+
Expected<Optional<std::unique_ptr<MemoryBuffer>>>
86+
SimpleObjectCache::getCachedObject(const Module &M) const {
87+
auto R = getCacheFileName(M.getModuleIdentifier());
88+
if (!R.hasValue())
89+
return None;
90+
91+
std::string F = std::move(R.getValue());
92+
if (!sys::fs::exists(F))
93+
return None;
94+
95+
auto B = MemoryBuffer::getFile(F, -1, false);
96+
if (!B)
97+
return createStringError(
98+
B.getError(),
99+
"Reading cached object '%s' failed with error code %d\n\n", F.c_str(),
100+
B.getError().value());
101+
102+
LLVM_DEBUG(dbgs() << format("Read cached object '%s'\n\n", F.c_str()));
103+
return std::forward<std::unique_ptr<MemoryBuffer>>(*B);
104+
}
105+
106+
void SimpleObjectCache::setCacheModuleName(Module &M) const {
107+
if (Enabled && !M.getName().startswith("file:"))
108+
M.setModuleIdentifier("file:" + M.getModuleIdentifier() + ".o");
109+
}
110+
111+
Optional<std::string>
112+
SimpleObjectCache::getCacheFileName(StringRef ModID) const {
113+
if (!Enabled)
114+
return None;
115+
116+
StringRef Prefix = "file:";
117+
if (!ModID.startswith(Prefix))
118+
return None;
119+
120+
std::string Name = Twine(CacheDir + ModID.substr(Prefix.size())).str();
121+
size_t DotPos = Name.rfind('.');
122+
if (DotPos != std::string::npos)
123+
Name.replace(DotPos, Name.size() - DotPos, ".o");
124+
125+
return Name;
126+
}

SimpleObjectCache.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
3+
#include <llvm/ADT/Optional.h>
4+
#include <llvm/ADT/StringRef.h>
5+
#include <llvm/ExecutionEngine/ObjectCache.h>
6+
7+
#include <memory>
8+
#include <string>
9+
10+
// Simple name-based lookup, based on lli's implementation
11+
class SimpleObjectCache : public llvm::ObjectCache {
12+
public:
13+
SimpleObjectCache(std::string Dir);
14+
~SimpleObjectCache() = default;
15+
16+
void setCacheModuleName(llvm::Module &M) const;
17+
18+
llvm::Expected<llvm::Optional<std::unique_ptr<llvm::MemoryBuffer>>>
19+
getCachedObject(const llvm::Module &M) const;
20+
21+
protected:
22+
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module *M) override;
23+
void notifyObjectCompiled(const llvm::Module *M,
24+
llvm::MemoryBufferRef Obj) override;
25+
26+
private:
27+
bool Enabled;
28+
std::string CacheDir;
29+
30+
static std::string endWithSeparator(std::string Path);
31+
llvm::Optional<std::string> getCacheFileName(llvm::StringRef ModID) const;
32+
};

main.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ static cl::opt<char>
2727
"(default = '-O2')"),
2828
cl::Prefix, cl::ZeroOrMore, cl::init(' '));
2929

30+
static cl::opt<std::string>
31+
CacheDir("cache-dir",
32+
cl::desc("Pass a directory name to enable object file cache"),
33+
cl::value_desc("directory"), cl::init(""));
34+
3035
Expected<unsigned> getOptLevel() {
3136
switch (OptLevel) {
3237
case '0': return 0;
@@ -141,7 +146,7 @@ int main(int argc, char **argv) {
141146
int x[]{0, 1, 2};
142147
int y[]{3, 1, -1};
143148

144-
JitFromScratch Jit(ExitOnErr);
149+
JitFromScratch Jit(ExitOnErr, CacheDir);
145150

146151
LLVM_DEBUG(dbgs() << "JITing for host target: "
147152
<< Jit.getTargetTriple().normalize() << "\n\n");
@@ -152,8 +157,9 @@ int main(int argc, char **argv) {
152157

153158
std::string JitedFnName = ExitOnErr(codegenIR(*M, arrayElements(x)));
154159
unsigned OptLevel = ExitOnErr(getOptLevel());
160+
bool AddToCache = !CacheDir.empty();
155161

156-
ExitOnErr(Jit.submitModule(std::move(M), std::move(C), OptLevel));
162+
ExitOnErr(Jit.submitModule(std::move(M), std::move(C), OptLevel, AddToCache));
157163

158164
// Request function; this compiles to machine code and links.
159165
auto integerDistances =

test/objcache.test-debug

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# RUN: rm -rf cache/
2+
3+
# Object cache is disabled by default
4+
# RUN: JitFromScratch -debug -debug-only=jitfromscratch 2>&1 | FileCheck -check-prefix=CACHELESS-CHECK %s
5+
# CACHELESS-CHECK-NOT: Read cached object 'cache/JitFromScratch.o'
6+
# CACHELESS-CHECK-NOT: Write cached object 'cache/JitFromScratch.o'
7+
# CACHELESS-CHECK: Integer Distances: 3, 0, 3
8+
9+
# Object files are written to the cache
10+
# RUN: JitFromScratch -cache-dir=cache/ -debug -debug-only=jitfromscratch 2>&1 | FileCheck -check-prefix=WRITE-CHECK %s
11+
# WRITE-CHECK-NOT: Read cached object 'cache/JitFromScratch.o'
12+
# WRITE-CHECK: Create new cache directory 'cache/'
13+
# WRITE-CHECK: Write cached object 'cache/JitFromScratch.o'
14+
# WRITE-CHECK: Integer Distances: 3, 0, 3
15+
16+
# Object files are read from the cache
17+
# RUN: JitFromScratch -cache-dir=cache/ -debug -debug-only=jitfromscratch 2>&1 | FileCheck -check-prefix=READ-CHECK %s
18+
# READ-CHECK: Read cached object 'cache/JitFromScratch.o'
19+
# READ-CHECK-NOT: Write cached object 'cache/JitFromScratch.o'
20+
# READ-CHECK: Integer Distances: 3, 0, 3

0 commit comments

Comments
 (0)