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

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

Set default segement-size to 8; PACK_LANES to 2; clean out cache file removal message

File size: 10.1 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_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;
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
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
81bool ParabixObjectCache::loadCachedObjectFile(const std::unique_ptr<kernel::KernelBuilder> & idb, kernel::Kernel * const kernel) {
82    if (LLVM_LIKELY(kernel->isCachable())) {
83        assert (kernel->getModule() == nullptr);
84        const auto moduleId = kernel->getCacheName(idb);
85
86        // Have we already seen this module before?
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);
91            kernel->prepareCachedKernel(idb);
92            return true;
93        }
94
95        // No, check for an existing cache file.
96        Path objectName(mCachePath);
97        sys::path::append(objectName, CACHE_PREFIX);
98        objectName.append(moduleId);
99        objectName.append(".o");
100
101        auto objectBuffer = MemoryBuffer::getFile(objectName.c_str(), -1, false);
102        if (objectBuffer) {
103            if (kernel->hasSignature()) {
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();
108                    if (LLVM_UNLIKELY(!loadedSig.equals(kernel->makeSignature(idb)))) {
109                        goto invalid;
110                    }
111                } else {
112                   
113                    report_fatal_error("signature file expected but not found: " + moduleId);
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);
121#if LLVM_VERSION_INTEGER < LLVM_4_0_0
122                auto loadedFile = getLazyBitcodeModule(std::move(kernelBuffer.get()), idb->getContext());
123#else
124                auto loadedFile = getOwningLazyBitcodeModule(std::move(kernelBuffer.get()), idb->getContext());
125#endif
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())));
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);
136                    sys::path::replace_extension(objectName, ".o");
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                    }
142                    return true;
143                }
144            }
145        }
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        }
159    }
160    return false;
161}
162
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) {
166    if (LLVM_LIKELY(M->getNamedMetadata(CACHEABLE))) {
167        const auto moduleId = M->getModuleIdentifier();
168        Path objectName(mCachePath);
169        sys::path::append(objectName, CACHE_PREFIX);
170        objectName.append(moduleId);
171        objectName.append(".o");
172
173        // Write the object code
174        std::error_code EC;
175        raw_fd_ostream objFile(objectName, EC, sys::fs::F_None);
176        objFile.write(Obj.getBufferStart(), Obj.getBufferSize());
177        objFile.close();
178
179        // then the signature (if one exists)
180        const MDString * const sig = getSignature(M);
181        if (sig) {
182            sys::path::replace_extension(objectName, ".sig");
183            raw_fd_ostream sigfile(objectName, EC, sys::fs::F_None);
184            sigfile << sig->getString();
185            sigfile.close();
186        }
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);
198        WriteBitcodeToFile(header.get(), kernelFile);
199        kernelFile.close();
200    }
201}
202
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            }
217        }
218    }
219}
220
221std::unique_ptr<MemoryBuffer> ParabixObjectCache::getObject(const Module * module) {
222    const auto f = mCachedObject.find(module->getModuleIdentifier());
223    if (f == mCachedObject.end()) {
224        return nullptr;
225    }
226    // Return a copy of the buffer, for MCJIT to modify, if necessary.
227    return MemoryBuffer::getMemBufferCopy(f->second.second.get()->getBuffer());
228}
229
230inline std::string ParabixObjectCache::getDefaultPath() {
231    // $HOME/.cache/parabix/
232    Path cachePath;
233#if LLVM_VERSION_INTEGER < LLVM_3_7_0
234    sys::path::user_cache_directory(cachePath, "parabix");
235#else
236    sys::path::home_directory(cachePath);
237    sys::path::append(cachePath, ".cache", "parabix");
238#endif
239    return cachePath.str();
240}
241
242ParabixObjectCache::ParabixObjectCache(const std::string dir)
243: mCachePath(dir) {
244    boost::filesystem::path p(mCachePath.str());
245    if (LLVM_LIKELY(!mCachePath.empty())) {
246        sys::fs::create_directories(Twine(mCachePath));
247    }
248    boost::filesystem::directory_iterator it(p);
249    mCacheCleanupIterator = it;
250    mCacheEntryMaxHours = CACHE_ENTRY_MAX_HOURS;
251}
252
253ParabixObjectCache::ParabixObjectCache()
254: ParabixObjectCache(getDefaultPath()) {
255}
256
257
Note: See TracBrowser for help on using the repository browser.