source: icGREP/icgrep-devel/icgrep/toolchain/object_cache.cpp @ 5841

Last change on this file since 5841 was 5841, checked in by cameron, 19 months ago

LLVM_VERSION_CODE macro, CC-multiplex option, performance bug fixes

File size: 9.9 KB
Line 
1#include "toolchain.h"
2#include "object_cache.h"
3#include <kernels/kernel.h>
4#include <kernels/kernel_builder.h>
5#include <llvm/Support/raw_ostream.h>
6#include <llvm/Support/MemoryBuffer.h>
7#include <llvm/IR/Metadata.h>
8#include <llvm/Support/FileSystem.h>
9#include <llvm/Support/Path.h>
10#include <llvm/Support/Debug.h>
11#include <llvm/IR/Module.h>
12#include <sys/file.h>
13#include <sys/stat.h>
14#include <fcntl.h>
15#include <boost/filesystem.hpp>
16#include <boost/range/iterator_range.hpp>
17#include <boost/container/flat_set.hpp>
18#if LLVM_VERSION_INTEGER < LLVM_VERSION_CODE(4, 0, 0)
19#include <llvm/Bitcode/ReaderWriter.h>
20#else
21#include <llvm/Bitcode/BitcodeReader.h>
22#include <llvm/Bitcode/BitcodeWriter.h>
23#endif
24#include <llvm/IR/Verifier.h>
25#include <ctime>
26
27using namespace llvm;
28namespace fs = boost::filesystem;
29
30#ifdef NDEBUG
31#define CACHE_ENTRY_MAX_HOURS (24 * codegen::CacheDaysLimit)
32#else
33#define CACHE_ENTRY_MAX_HOURS (1)
34#endif
35
36#define SECONDS_PER_HOUR (3600)
37//===----------------------------------------------------------------------===//
38// Object cache (based on tools/lli/lli.cpp, LLVM 3.6.1)
39//
40// This object cache implementation writes cached objects to disk to the
41// directory specified by CacheDir, using a filename provided in the module
42// descriptor. The cache tries to load a saved object using that path if the
43// file exists.
44//
45
46#define MONTH_1 \
47    ((__DATE__ [0] == 'O' || __DATE__ [0] == 'N' || __DATE__ [0] == 'D') ? '1' : '0')
48#define MONTH_2 \
49    (__DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? '1' : '6') \
50    : __DATE__ [2] == 'b' ? '2' \
51    : __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? '3' : '4') \
52    : __DATE__ [2] == 'y' ? '5' \
53    : __DATE__ [2] == 'l' ? '7' \
54    : __DATE__ [2] == 'g' ? '8' \
55    : __DATE__ [2] == 'p' ? '9' \
56    : __DATE__ [2] == 't' ? '0' \
57    : __DATE__ [2] == 'v' ? '1' : '2')
58#define DAY_1 (__DATE__[4] == ' ' ? '0' : __DATE__[4])
59#define DAY_2 (__DATE__[5])
60#define YEAR_1 (__DATE__[9])
61#define YEAR_2 (__DATE__[10])
62#define HOUR_1 (__TIME__[0])
63#define HOUR_2 (__TIME__[1])
64#define MINUTE_1 (__TIME__[3])
65#define MINUTE_2 (__TIME__[4])
66#define SECOND_1 (__TIME__[6])
67#define SECOND_2 (__TIME__[7])
68
69const static auto CACHE_PREFIX = PARABIX_VERSION +
70                          std::string{'@',
71                          MONTH_1, MONTH_2, DAY_1, DAY_2, YEAR_1, YEAR_2,
72                          HOUR_1, HOUR_2, MINUTE_1, MINUTE_2, SECOND_1, SECOND_2,
73                          '_'};
74
75const static auto CACHEABLE = "cacheable";
76
77const static auto SIGNATURE = "signature";
78
79const MDString * getSignature(const llvm::Module * const M) {
80    NamedMDNode * const sig = M->getNamedMetadata(SIGNATURE);
81    if (sig) {
82        assert ("empty metadata node" && sig->getNumOperands() == 1);
83        assert ("no signature payload" && sig->getOperand(0)->getNumOperands() == 1);
84        return dyn_cast<MDString>(sig->getOperand(0)->getOperand(0));
85    }
86    return nullptr;
87}
88
89bool ParabixObjectCache::loadCachedObjectFile(const std::unique_ptr<kernel::KernelBuilder> & idb, kernel::Kernel * const kernel) {
90    if (LLVM_LIKELY(kernel->isCachable())) {
91        assert (kernel->getModule() == nullptr);
92        const auto moduleId = kernel->getCacheName(idb);
93
94        // Have we already seen this module before?
95        const auto f = mCachedObject.find(moduleId);
96        if (LLVM_UNLIKELY(f != mCachedObject.end())) {
97            Module * const m = f->second.first; assert (m);
98            kernel->setModule(m);
99            kernel->prepareCachedKernel(idb);
100            return true;
101        }
102
103        // No, check for an existing cache file.
104        Path fileName(mCachePath);
105        sys::path::append(fileName, CACHE_PREFIX);
106        fileName.append(moduleId);
107        fileName.append(".kernel");
108
109        auto kernelBuffer = MemoryBuffer::getFile(fileName.c_str(), -1, false);
110        if (kernelBuffer) {
111            #if LLVM_VERSION_INTEGER < LLVM_VERSION_CODE(4, 0, 0)
112            auto loadedFile = getLazyBitcodeModule(std::move(kernelBuffer.get()), idb->getContext());
113            #else
114            auto loadedFile = getOwningLazyBitcodeModule(std::move(kernelBuffer.get()), idb->getContext());
115            #endif
116            // if there was no error when parsing the bitcode
117            if (LLVM_LIKELY(loadedFile)) {
118                std::unique_ptr<Module> M(std::move(loadedFile.get()));
119                if (kernel->hasSignature()) {
120                    const MDString * const sig = getSignature(M.get());
121                    assert ("signature is missing from kernel file: possible module naming conflict or change in the LLVM metadata storage policy?" && sig);
122                    if (LLVM_UNLIKELY(sig == nullptr || !sig->getString().equals(kernel->makeSignature(idb)))) {
123                        goto invalid;
124                    }
125                }
126                sys::path::replace_extension(fileName, ".o");
127                auto objectBuffer = MemoryBuffer::getFile(fileName.c_str(), -1, false);
128                if (LLVM_LIKELY(objectBuffer)) {
129                    Module * const m = M.release();
130                    // defaults to <path>/<moduleId>.kernel
131                    m->setModuleIdentifier(moduleId);
132                    kernel->setModule(m);
133                    kernel->prepareCachedKernel(idb);
134                    mCachedObject.emplace(moduleId, std::make_pair(m, std::move(objectBuffer.get())));
135                    // update the modified time of the .kernel, .o and .sig files
136                    time_t access_time = time(0);
137                    fs::last_write_time(fileName.c_str(), access_time);
138                    sys::path::replace_extension(fileName, ".kernel");
139                    fs::last_write_time(fileName.c_str(), access_time);
140                    return true;
141                }
142            }
143        }
144
145invalid:
146
147        Module * const module = kernel->setModule(new Module(moduleId, idb->getContext()));
148        // mark this module as cachable
149        module->getOrInsertNamedMetadata(CACHEABLE);
150        // if this module has a signature, add it to the metadata
151        if (kernel->hasSignature()) {
152            NamedMDNode * const md = module->getOrInsertNamedMetadata(SIGNATURE);
153            assert (md->getNumOperands() == 0);
154            MDString * const sig = MDString::get(module->getContext(), kernel->makeSignature(idb));
155            md->addOperand(MDNode::get(module->getContext(), {sig}));
156        }
157    }
158    return false;
159}
160
161// A new module has been compiled. If it is cacheable and no conflicting module
162// exists, write it out.
163void ParabixObjectCache::notifyObjectCompiled(const Module * M, MemoryBufferRef Obj) {
164    if (LLVM_LIKELY(M->getNamedMetadata(CACHEABLE))) {
165        const auto moduleId = M->getModuleIdentifier();
166        Path objectName(mCachePath);
167        sys::path::append(objectName, CACHE_PREFIX);
168        objectName.append(moduleId);
169        objectName.append(".o");
170
171        // Write the object code
172        std::error_code EC;
173        raw_fd_ostream objFile(objectName, EC, sys::fs::F_None);
174        objFile.write(Obj.getBufferStart(), Obj.getBufferSize());
175        objFile.close();
176
177        // and kernel prototype header
178        std::unique_ptr<Module> H(new Module(M->getModuleIdentifier(), M->getContext()));
179        for (const Function & f : M->getFunctionList()) {
180            if (f.hasExternalLinkage() && !f.empty()) {
181                Function::Create(f.getFunctionType(), Function::ExternalLinkage, f.getName(), H.get());
182            }
183        }
184
185        // then the signature (if one exists)
186        const MDString * const sig = getSignature(M);
187        if (sig) {
188            NamedMDNode * const md = H->getOrInsertNamedMetadata(SIGNATURE);
189            assert (md->getNumOperands() == 0);
190            MDString * const sigCopy = MDString::get(H->getContext(), sig->getString());
191            md->addOperand(MDNode::get(H->getContext(), {sigCopy}));
192        }
193
194        sys::path::replace_extension(objectName, ".kernel");
195        raw_fd_ostream kernelFile(objectName.str(), EC, sys::fs::F_None);
196        WriteBitcodeToFile(H.get(), kernelFile);
197        kernelFile.close();
198    }
199}
200
201void ParabixObjectCache::performIncrementalCacheCleanupStep() {
202    mCleanupMutex.lock();
203    if (LLVM_UNLIKELY(mCleanupIterator == fs::directory_iterator())) {
204        mCleanupMutex.unlock();
205    } else {
206        const auto e = mCleanupIterator->path();
207        mCleanupIterator++;
208        mCleanupMutex.unlock();
209
210        // Simple clean-up policy: files that haven't been touched by the
211        // driver in MaxCacheEntryHours are deleted.
212        // TODO: possibly incrementally manage by size and/or total file count.
213        // TODO: possibly determine total filecount and set items per clean up step based on
214        // filecount
215        if (fs::is_regular_file(e)) {
216            const auto age = std::time(nullptr) - fs::last_write_time(e);
217            if (age > (CACHE_ENTRY_MAX_HOURS * SECONDS_PER_HOUR)) {
218                fs::remove(e);
219            }
220        }
221    }
222}
223
224std::unique_ptr<MemoryBuffer> ParabixObjectCache::getObject(const Module * module) {
225    const auto f = mCachedObject.find(module->getModuleIdentifier());
226    if (f == mCachedObject.end()) {
227        return nullptr;
228    }
229    // Return a copy of the buffer, for MCJIT to modify, if necessary.
230    return MemoryBuffer::getMemBufferCopy(f->second.second.get()->getBuffer());
231}
232
233ParabixObjectCache::ParabixObjectCache(const StringRef dir)
234: mCachePath(dir) {
235    fs::path p(mCachePath.str());
236    if (LLVM_LIKELY(!mCachePath.empty())) {
237        sys::fs::create_directories(mCachePath);
238    }
239    mCleanupIterator = fs::directory_iterator(p);
240}
241
242inline ParabixObjectCache::Path getDefaultPath() {
243    // $HOME/.cache/parabix/
244    ParabixObjectCache::Path cachePath;
245#if LLVM_VERSION_INTEGER < LLVM_VERSION_CODE(3, 7, 0)
246    sys::path::user_cache_directory(cachePath, "parabix");
247#else
248    sys::path::home_directory(cachePath);
249    sys::path::append(cachePath, ".cache", "parabix");
250#endif
251    return cachePath;
252}
253
254ParabixObjectCache::ParabixObjectCache()
255: ParabixObjectCache(getDefaultPath()) {
256
257}
258
259
Note: See TracBrowser for help on using the repository browser.