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

Last change on this file since 5735 was 5735, checked in by cameron, 18 months ago

Incremental object caching; multithreaded print

File size: 10.2 KB
RevLine 
[5732]1#include "toolchain.h"
[4962]2#include "object_cache.h"
[5431]3#include <kernels/kernel.h>
[5464]4#include <kernels/kernel_builder.h>
[4962]5#include <llvm/Support/raw_ostream.h>
[5431]6#include <llvm/Support/MemoryBuffer.h>
[5464]7#include <llvm/IR/Metadata.h>
[4962]8#include <llvm/Support/FileSystem.h>
9#include <llvm/Support/Path.h>
[5735]10#include <llvm/Support/Debug.h>
[5267]11#include <llvm/IR/Module.h>
[5431]12#include <sys/file.h>
13#include <sys/stat.h>
14#include <fcntl.h>
15#include <boost/filesystem.hpp>
[5464]16#include <boost/range/iterator_range.hpp>
[5630]17#include <boost/container/flat_set.hpp>
[5732]18#if LLVM_VERSION_INTEGER < LLVM_4_0_0
[5630]19#include <llvm/Bitcode/ReaderWriter.h>
[5732]20#else
21#include <llvm/Bitcode/BitcodeReader.h>
22#include <llvm/Bitcode/BitcodeWriter.h>
23#endif
[5630]24#include <llvm/IR/Verifier.h>
[5431]25#include <ctime>
[5391]26
[4962]27using namespace llvm;
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
[4964]35// file exists.
[4962]36//
[4964]37
[5435]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])
[5431]60
[5435]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                          '_'};
[4962]66
[5464]67const static auto CACHEABLE = "cacheable";
68
69const static auto SIGNATURE = "signature";
70
71const MDString * getSignature(const llvm::Module * const M) {
72    NamedMDNode * const sig = M->getNamedMetadata(SIGNATURE);
73    if (sig) {
74        assert ("empty metadata node" && sig->getNumOperands() == 1);
75        assert ("no signature payload" && sig->getOperand(0)->getNumOperands() == 1);
76        return cast<MDString>(sig->getOperand(0)->getOperand(0));
77    }
78    return nullptr;
79}
80
[5440]81bool ParabixObjectCache::loadCachedObjectFile(const std::unique_ptr<kernel::KernelBuilder> & idb, kernel::Kernel * const kernel) {
[5431]82    if (LLVM_LIKELY(kernel->isCachable())) {
[5630]83        assert (kernel->getModule() == nullptr);
84        const auto moduleId = kernel->getCacheName(idb);
85
[5431]86        // Have we already seen this module before?
[5630]87        const auto f = mCachedObject.find(moduleId);
88        if (LLVM_UNLIKELY(f != mCachedObject.end())) {
89            Module * const m = f->second.first; assert (m);
90            kernel->setModule(m);
[5646]91            kernel->prepareCachedKernel(idb);
[5431]92            return true;
93        }
94
95        // No, check for an existing cache file.
96        Path objectName(mCachePath);
[5435]97        sys::path::append(objectName, CACHE_PREFIX);
98        objectName.append(moduleId);
99        objectName.append(".o");
100
[5431]101        auto objectBuffer = MemoryBuffer::getFile(objectName.c_str(), -1, false);
102        if (objectBuffer) {
[5464]103            if (kernel->hasSignature()) {
[5431]104                sys::path::replace_extension(objectName, ".sig");
105                const auto signatureBuffer = MemoryBuffer::getFile(objectName.c_str(), -1, false);
106                if (signatureBuffer) {
107                    const StringRef loadedSig = signatureBuffer.get()->getBuffer();
[5630]108                    if (LLVM_UNLIKELY(!loadedSig.equals(kernel->makeSignature(idb)))) {
109                        goto invalid;
[5431]110                    }
111                } else {
[5735]112                   
[5431]113                    report_fatal_error("signature file expected but not found: " + moduleId);
[5630]114                }               
115            }
116            sys::path::replace_extension(objectName, ".kernel");
117            auto kernelBuffer = MemoryBuffer::getFile(objectName.c_str(), -1, false);
118            if (*kernelBuffer) {
119                //MemoryBuffer * kb = kernelBuffer.get().release();
120                //auto loadedFile = parseBitcodeFile(kb->getMemBufferRef(), mContext);
[5732]121#if LLVM_VERSION_INTEGER < LLVM_4_0_0
[5630]122                auto loadedFile = getLazyBitcodeModule(std::move(kernelBuffer.get()), idb->getContext());
[5732]123#else
124                auto loadedFile = getOwningLazyBitcodeModule(std::move(kernelBuffer.get()), idb->getContext());
125#endif
[5630]126                if (*loadedFile) {
127                    Module * const m = loadedFile.get().release(); assert (m);
128                    // defaults to <path>/<moduleId>.kernel
129                    m->setModuleIdentifier(moduleId);
130                    kernel->setModule(m);
131                    kernel->prepareCachedKernel(idb);                   
132                    mCachedObject.emplace(moduleId, std::make_pair(m, std::move(objectBuffer.get())));
[5735]133                    // update the modified time of the .kernel, .o and .sig files
134                    time_t access_time = time(0);
135                    boost::filesystem::last_write_time(objectName.c_str(), access_time);
[5630]136                    sys::path::replace_extension(objectName, ".o");
[5735]137                    boost::filesystem::last_write_time(objectName.c_str(), access_time);
138                    if (kernel->hasSignature()) {
139                        sys::path::replace_extension(objectName, ".sig");
140                        boost::filesystem::last_write_time(objectName.c_str(), access_time);
141                    }
[5630]142                    return true;
[5431]143                }
144            }
145        }
[5630]146
147invalid:
148
149        Module * const module = kernel->setModule(new Module(moduleId, idb->getContext()));
150        // mark this module as cachable
151        module->getOrInsertNamedMetadata(CACHEABLE);
152        // if this module has a signature, add it to the metadata
153        if (kernel->hasSignature()) {
154            NamedMDNode * const md = module->getOrInsertNamedMetadata(SIGNATURE);
155            assert (md->getNumOperands() == 0);
156            MDString * const sig = MDString::get(module->getContext(), kernel->makeSignature(idb));
157            md->addOperand(MDNode::get(module->getContext(), {sig}));
158        }
[5394]159    }
[5431]160    return false;
[4962]161}
162
[5431]163// A new module has been compiled. If it is cacheable and no conflicting module
164// exists, write it out.
165void ParabixObjectCache::notifyObjectCompiled(const Module * M, MemoryBufferRef Obj) {
[5630]166    if (LLVM_LIKELY(M->getNamedMetadata(CACHEABLE))) {
[5464]167        const auto moduleId = M->getModuleIdentifier();
[5431]168        Path objectName(mCachePath);
[5435]169        sys::path::append(objectName, CACHE_PREFIX);
170        objectName.append(moduleId);
171        objectName.append(".o");
[5402]172
[5630]173        // Write the object code
[5431]174        std::error_code EC;
[5630]175        raw_fd_ostream objFile(objectName, EC, sys::fs::F_None);
176        objFile.write(Obj.getBufferStart(), Obj.getBufferSize());
177        objFile.close();
[5431]178
[5630]179        // then the signature (if one exists)
[5464]180        const MDString * const sig = getSignature(M);
181        if (sig) {
[5431]182            sys::path::replace_extension(objectName, ".sig");
183            raw_fd_ostream sigfile(objectName, EC, sys::fs::F_None);
[5464]184            sigfile << sig->getString();
[5431]185            sigfile.close();
[5394]186        }
[5630]187
188        // and finally kernel prototype header.
189        std::unique_ptr<Module> header(new Module(M->getModuleIdentifier(), M->getContext()));
190        for (const Function & f : M->getFunctionList()) {
191            if (f.hasExternalLinkage() && !f.empty()) {
192                Function::Create(f.getFunctionType(), Function::ExternalLinkage, f.getName(), header.get());
193            }
194        }
195
196        sys::path::replace_extension(objectName, ".kernel");
197        raw_fd_ostream kernelFile(objectName.str(), EC, sys::fs::F_None);
[5731]198        WriteBitcodeToFile(header.get(), kernelFile);
[5630]199        kernelFile.close();
[5394]200    }
[5391]201}
202
[5735]203void ParabixObjectCache::performIncrementalCacheCleanupStep() {
204    if (mCacheCleanupIterator != boost::filesystem::directory_iterator()) {
205        auto & e = mCacheCleanupIterator->path();
206        mCacheCleanupIterator++;
207        // Simple clean-up policy: files that haven't been touched by the
208        // driver in MaxCacheEntryHours are deleted.
209        // TODO: possibly incrementally manage by size and/or total file count.
210        // TODO: possibly determine total filecount and set items per clean up step based on
211        // filecount
212        if (boost::filesystem::is_regular_file(e)) {
213            auto age = std::time(nullptr) - boost::filesystem::last_write_time(e);
214            if (age > mCacheEntryMaxHours * 3600 /* secs/hour*/ ) {
215                boost::filesystem::remove(e);
216                errs() << e.string() << " removed.\n";
[5464]217            }
218        }
219    }
[5394]220}
[5391]221
[5464]222std::unique_ptr<MemoryBuffer> ParabixObjectCache::getObject(const Module * module) {
[5630]223    const auto f = mCachedObject.find(module->getModuleIdentifier());
[5464]224    if (f == mCachedObject.end()) {
[4962]225        return nullptr;
[5391]226    }
227    // Return a copy of the buffer, for MCJIT to modify, if necessary.
[5630]228    return MemoryBuffer::getMemBufferCopy(f->second.second.get()->getBuffer());
[4962]229}
230
[5735]231inline std::string ParabixObjectCache::getDefaultPath() {
[5435]232    // $HOME/.cache/parabix/
233    Path cachePath;
[5731]234#if LLVM_VERSION_INTEGER < LLVM_3_7_0
[5735]235    sys::path::user_cache_directory(cachePath, "parabix");
[5731]236#else
[5435]237    sys::path::home_directory(cachePath);
[5735]238    sys::path::append(cachePath, ".cache", "parabix");
[5731]239#endif
[5735]240    return cachePath.str();
[5435]241}
242
[5735]243ParabixObjectCache::ParabixObjectCache(const std::string dir)
244: mCachePath(dir) {
245    boost::filesystem::path p(mCachePath.str());
246    if (LLVM_LIKELY(!mCachePath.empty())) {
247        sys::fs::create_directories(Twine(mCachePath));
248    }
249    boost::filesystem::directory_iterator it(p);
250    mCacheCleanupIterator = it;
251    mCacheEntryMaxHours = CACHE_ENTRY_MAX_HOURS;
252}
253
[5435]254ParabixObjectCache::ParabixObjectCache()
[5735]255: ParabixObjectCache(getDefaultPath()) {
[5435]256}
257
258
Note: See TracBrowser for help on using the repository browser.