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

Last change on this file since 6237 was 6212, checked in by nmedfort, 8 months ago

Cache name issue fix

File size: 15.7 KB
Line 
1#include "object_cache.h"
2#include "object_cache_util.hpp"
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 <toolchain/toolchain.h>
13#if LLVM_VERSION_INTEGER < LLVM_VERSION_CODE(4, 0, 0)
14#include <llvm/Bitcode/ReaderWriter.h>
15#else
16#include <llvm/Bitcode/BitcodeReader.h>
17#include <llvm/Bitcode/BitcodeWriter.h>
18#endif
19#include <llvm/IR/Verifier.h>
20#include <boost/lexical_cast.hpp>
21
22using namespace llvm;
23using namespace boost;
24
25using Path = ParabixObjectCache::Path;
26
27std::unique_ptr<ParabixObjectCache> ParabixObjectCache::mInstance;
28
29//===----------------------------------------------------------------------===//
30// Object cache (based on tools/lli/lli.cpp, LLVM 3.6.1)
31//
32// This object cache implementation writes cached objects to disk to the
33// directory specified by CacheDir, using a filename provided in the module
34// descriptor. The cache tries to load a saved object using that path if the
35// file exists.
36//
37
38#define MONTH_1 \
39    ((__DATE__ [0] == 'O' || __DATE__ [0] == 'N' || __DATE__ [0] == 'D') ? '1' : '0')
40#define MONTH_2 \
41    (__DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? '1' : '6') \
42    : __DATE__ [2] == 'b' ? '2' \
43    : __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? '3' : '4') \
44    : __DATE__ [2] == 'y' ? '5' \
45    : __DATE__ [2] == 'l' ? '7' \
46    : __DATE__ [2] == 'g' ? '8' \
47    : __DATE__ [2] == 'p' ? '9' \
48    : __DATE__ [2] == 't' ? '0' \
49    : __DATE__ [2] == 'v' ? '1' : '2')
50#define DAY_1 (__DATE__[4] == ' ' ? '0' : __DATE__[4])
51#define DAY_2 (__DATE__[5])
52#define YEAR_1 (__DATE__[9])
53#define YEAR_2 (__DATE__[10])
54#define HOUR_1 (__TIME__[0])
55#define HOUR_2 (__TIME__[1])
56#define MINUTE_1 (__TIME__[3])
57#define MINUTE_2 (__TIME__[4])
58#define SECOND_1 (__TIME__[6])
59#define SECOND_2 (__TIME__[7])
60
61const static auto CACHE_PREFIX = PARABIX_VERSION +
62                          std::string{'@',
63                          MONTH_1, MONTH_2, DAY_1, DAY_2, YEAR_1, YEAR_2,
64                          HOUR_1, HOUR_2, MINUTE_1, MINUTE_2, SECOND_1, SECOND_2,
65                          '_'};
66
67const static auto CACHEABLE = "cacheable";
68
69const static auto SIGNATURE = "signature";
70
71/** ------------------------------------------------------------------------------------------------------------- *
72 * @brief getSignature
73 ** ------------------------------------------------------------------------------------------------------------- */
74const MDString * getSignature(const llvm::Module * const M) {
75    NamedMDNode * const sig = M->getNamedMetadata(SIGNATURE);
76    if (sig) {
77        assert ("empty metadata node" && sig->getNumOperands() == 1);
78        assert ("no signature payload" && sig->getOperand(0)->getNumOperands() == 1);
79        return dyn_cast<MDString>(sig->getOperand(0)->getOperand(0));
80    }
81    return nullptr;
82}
83
84/** ------------------------------------------------------------------------------------------------------------- *
85 * @brief loadCachedObjectFile
86 ** ------------------------------------------------------------------------------------------------------------- */
87bool ParabixObjectCache::loadCachedObjectFile(const std::unique_ptr<kernel::KernelBuilder> & idb, kernel::Kernel * const kernel) {
88    if (LLVM_LIKELY(kernel->isCachable())) {
89        assert (kernel->getModule() == nullptr);
90        const auto moduleId = kernel->getCacheName(idb);
91
92        // TODO: To enable the quick lookup of previously cached objects, I need to reclaim ownership
93        // of the modules from the JIT engine before it destroys them.
94
95//        // Have we already seen this module before?
96//        const auto f = mCachedObject.find(moduleId);
97//        if (LLVM_UNLIKELY(f != mCachedObject.end())) {
98//            Module * const m = f->second.first; assert (m);
99//            kernel->setModule(m);
100//            kernel->prepareCachedKernel(idb);
101//            return true;
102//        }
103
104        // No, check for an existing cache file.
105        Path fileName(mCachePath);
106        sys::path::append(fileName, CACHE_PREFIX);
107        fileName.append(moduleId);
108        fileName.append(KERNEL_FILE_EXTENSION);
109
110        auto kernelBuffer = MemoryBuffer::getFile(fileName, -1, false);
111        if (kernelBuffer) {
112            #if LLVM_VERSION_INTEGER < LLVM_VERSION_CODE(4, 0, 0)
113            auto loadedFile = getLazyBitcodeModule(std::move(kernelBuffer.get()), idb->getContext());
114            #else
115            auto loadedFile = getOwningLazyBitcodeModule(std::move(kernelBuffer.get()), idb->getContext());
116            #endif
117            // if there was no error when parsing the bitcode
118            if (LLVM_LIKELY(loadedFile)) {
119                std::unique_ptr<Module> M(std::move(loadedFile.get()));
120                if (kernel->hasSignature()) {
121                    const MDString * const sig = getSignature(M.get());
122                    assert ("signature is missing from kernel file: possible module naming conflict or change in the LLVM metadata storage policy?" && sig);
123                    if (LLVM_UNLIKELY(sig == nullptr || !sig->getString().equals(kernel->makeSignature(idb)))) {
124                        goto invalid;
125                    }
126                }
127                sys::path::replace_extension(fileName, OBJECT_FILE_EXTENSION);
128                auto objectBuffer = MemoryBuffer::getFile(fileName.c_str(), -1, false);
129                if (LLVM_LIKELY(objectBuffer)) {
130                    Module * const m = M.release();
131                    // defaults to <path>/<moduleId>.kernel
132                    m->setModuleIdentifier(moduleId);
133                    kernel->setModule(m);
134                    kernel->prepareCachedKernel(idb);
135                    mCachedObject.emplace(moduleId, std::make_pair(m, std::move(objectBuffer.get())));
136                    // update the modified time of the .kernel, .o and .kernel files
137                    const auto access_time = currentTime();
138                    fs::last_write_time(fileName.c_str(), access_time);
139                    sys::path::replace_extension(fileName, KERNEL_FILE_EXTENSION);
140                    fs::last_write_time(fileName.c_str(), access_time);
141                    return true;
142                }
143            }
144        }
145
146invalid:
147
148        Module * const module = kernel->setModule(new Module(moduleId, idb->getContext()));
149        // mark this module as cachable
150        module->getOrInsertNamedMetadata(CACHEABLE);
151        // if this module has a signature, add it to the metadata
152        if (kernel->hasSignature()) {
153            NamedMDNode * const md = module->getOrInsertNamedMetadata(SIGNATURE);
154            assert (md->getNumOperands() == 0);
155            MDString * const sig = MDString::get(module->getContext(), kernel->makeSignature(idb));
156            md->addOperand(MDNode::get(module->getContext(), {sig}));
157        }
158    }
159    return false;
160}
161
162/** ------------------------------------------------------------------------------------------------------------- *
163 * @brief notifyObjectCompiled
164 *
165 * A new module has been compiled. If it is cacheable and no conflicting module exists, write it out.
166 ** ------------------------------------------------------------------------------------------------------------- */
167void ParabixObjectCache::notifyObjectCompiled(const Module * M, MemoryBufferRef Obj) {
168    if (LLVM_LIKELY(M->getNamedMetadata(CACHEABLE))) {
169
170        const auto moduleId = M->getModuleIdentifier();
171        Path objectName(mCachePath);
172        sys::path::append(objectName, CACHE_PREFIX);
173        objectName.append(moduleId);
174        objectName.append(OBJECT_FILE_EXTENSION);
175
176        // Write the object code
177        std::error_code EC;
178        raw_fd_ostream objFile(objectName, EC, sys::fs::F_None);
179        objFile.write(Obj.getBufferStart(), Obj.getBufferSize());
180        objFile.close();
181
182        // and kernel prototype header
183        std::unique_ptr<Module> H(new Module(M->getModuleIdentifier(), M->getContext()));
184        for (const Function & f : M->getFunctionList()) {
185            if (f.hasExternalLinkage() && !f.empty()) {
186                Function::Create(f.getFunctionType(), Function::ExternalLinkage, f.getName(), H.get());
187            }
188        }
189
190        // then the signature (if one exists)
191        const MDString * const sig = getSignature(M);
192        if (sig) {
193            NamedMDNode * const md = H->getOrInsertNamedMetadata(SIGNATURE);
194            assert (md->getNumOperands() == 0);
195            MDString * const sigCopy = MDString::get(H->getContext(), sig->getString());
196            md->addOperand(MDNode::get(H->getContext(), {sigCopy}));
197        }
198
199        sys::path::replace_extension(objectName, KERNEL_FILE_EXTENSION);
200        raw_fd_ostream kernelFile(objectName.str(), EC, sys::fs::F_None);
201        WriteBitcodeToFile(H.get(), kernelFile);
202        kernelFile.close();
203    }
204}
205
206/** ------------------------------------------------------------------------------------------------------------- *
207 * @brief getObject
208 ** ------------------------------------------------------------------------------------------------------------- */
209std::unique_ptr<MemoryBuffer> ParabixObjectCache::getObject(const Module * module) {
210    const auto f = mCachedObject.find(module->getModuleIdentifier());
211    if (f == mCachedObject.end()) {
212        return nullptr;
213    }
214    // Return a copy of the buffer, for MCJIT to modify, if necessary.
215    return MemoryBuffer::getMemBufferCopy(f->second.second.get()->getBuffer());
216}
217
218/** ------------------------------------------------------------------------------------------------------------- *
219 * @brief checkForCachedKernel
220 ** ------------------------------------------------------------------------------------------------------------- */
221bool ParabixObjectCache::checkForCachedKernel(const std::unique_ptr<kernel::KernelBuilder> & b, kernel::Kernel * const kernel) noexcept {
222    return mInstance.get() && mInstance->loadCachedObjectFile(b, kernel);
223}
224
225/** ------------------------------------------------------------------------------------------------------------- *
226 * @brief requiresCacheCleanUp
227 ** ------------------------------------------------------------------------------------------------------------- */
228inline bool ParabixObjectCache::requiresCacheCleanUp() noexcept {
229    return FileLock(fs::path{mCachePath.str()}).locked();
230}
231
232/** ------------------------------------------------------------------------------------------------------------- *
233 * @brief initiateCacheCleanUp
234 ** ------------------------------------------------------------------------------------------------------------- */
235void ParabixObjectCache::initiateCacheCleanUp() noexcept {
236    if (LLVM_UNLIKELY(requiresCacheCleanUp())) {
237        // syslog?
238        if (fork() == 0) {
239            char * const cachePath = const_cast<char *>(mCachePath.c_str());
240            char * args[3] = {const_cast<char *>(CACHE_JANITOR_FILE_NAME), cachePath, nullptr};
241            Path janitorFileName(codegen::ProgramName);
242            sys::path::remove_filename(janitorFileName);
243            sys::path::append(janitorFileName, CACHE_JANITOR_FILE_NAME);
244            char * const janitorPath = const_cast<char *>(janitorFileName.c_str());
245            if (execvp(janitorPath, args) < 0) {
246                // syslog?
247            }
248        }
249    }
250}
251
252/** ------------------------------------------------------------------------------------------------------------- *
253 * @brief getDefaultCachePath
254 ** ------------------------------------------------------------------------------------------------------------- */
255inline void getDefaultCachePath(Path & configPath) {
256    // default: $HOME/.cache/parabix/
257    sys::path::home_directory(configPath);
258    sys::path::append(configPath, ".cache", "parabix");
259}
260
261#if 0
262
263/** ------------------------------------------------------------------------------------------------------------- *
264 * @brief getConfigPath
265 ** ------------------------------------------------------------------------------------------------------------- */
266inline Path getConfigPath() {
267    // $HOME/.config/parabix/cache.cfg
268    Path configPath;
269    sys::path::home_directory(configPath);
270    sys::path::append(configPath, ".config", "parabix");
271    sys::fs::create_directories(configPath);
272    sys::path::append(configPath, "cache.cfg");
273    return configPath;
274}
275
276/** ------------------------------------------------------------------------------------------------------------- *
277 * @brief loadCacheSettings
278 ** ------------------------------------------------------------------------------------------------------------- */
279inline size_t parseInt(const StringRef & str, const StringRef & label) {
280    try {
281        return lexical_cast<size_t>(str.data(), str.size());
282    } catch(const bad_lexical_cast &) {
283        errs() << "configuration for " << label << " must be an integer";
284        exit(-1);
285    }
286}
287
288#endif
289
290/** ------------------------------------------------------------------------------------------------------------- *
291 * @brief loadCacheSettings
292 ** ------------------------------------------------------------------------------------------------------------- */
293inline void ParabixObjectCache::loadCacheSettings() noexcept {
294    getDefaultCachePath(mCachePath);
295    #if 0
296
297    const auto configPath = getConfigPath();
298    auto configFile = MemoryBuffer::getFile(configPath);
299
300    // default: $HOME/.cache/parabix/
301    sys::path::home_directory(mCachePath);
302    sys::path::append(mCachePath, ".cache", "parabix");
303    // default: 1 week
304    mCacheExpirationDelay = CACHE_ENTRY_EXPIRY_PERIOD;
305
306    if (LLVM_UNLIKELY(!!configFile)) {
307        const StringRef config = (*configFile)->getBuffer();
308        #define ASCII_WHITESPACE " \f\n\r\t\v"
309        #define ASCII_WHITESPACE_OR_EQUALS (ASCII_WHITESPACE "+")
310        size_t nameStart = 0;
311        for (;;) {
312
313            const auto nameEnd = config.find_first_of(ASCII_WHITESPACE_OR_EQUALS, nameStart);
314            if (nameEnd == StringRef::npos) break;
315            const auto afterEquals = config.find_first_of('=', nameEnd) + 1;
316            if (LLVM_UNLIKELY(afterEquals == StringRef::npos)) break;
317            const auto valueStart = config.find_first_not_of(ASCII_WHITESPACE, afterEquals);
318            if (LLVM_UNLIKELY(valueStart == StringRef::npos)) break;
319            const auto valueEnd = config.find_first_of(ASCII_WHITESPACE, valueStart);
320            if (LLVM_UNLIKELY(valueEnd == StringRef::npos)) break;
321            const auto name = config.slice(nameStart, nameEnd);
322            const auto value = config.slice(valueStart, valueEnd);
323
324            if (name.equals_lower("cachepath")) {
325                mCachePath.assign(value);
326            } else if (name.equals_lower("cachedayslimit")) {
327                mCacheExpirationDelay = parseInt(value, "cachedayslimit");
328            }
329            // get the next name start
330            nameStart = config.find_first_not_of(ASCII_WHITESPACE, valueEnd + 1);
331        }
332    }
333    #endif
334    sys::fs::create_directories(mCachePath);
335}
336
337/** ------------------------------------------------------------------------------------------------------------- *
338 * @brief saveCachePath
339 ** ------------------------------------------------------------------------------------------------------------- */
340inline void ParabixObjectCache::saveCacheSettings() noexcept {
341
342
343}
344
345/** ------------------------------------------------------------------------------------------------------------- *
346 * @brief initializeCacheSystems
347 ** ------------------------------------------------------------------------------------------------------------- */
348void ParabixObjectCache::initializeCacheSystems() noexcept {
349    if (LLVM_LIKELY(mInstance.get() == nullptr && codegen::EnableObjectCache)) {
350        mInstance.reset(new ParabixObjectCache());
351    }
352}
353
354ParabixObjectCache::ParabixObjectCache() {
355    loadCacheSettings();
356    initiateCacheCleanUp();
357}
Note: See TracBrowser for help on using the repository browser.