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

Last change on this file since 5732 was 5732, checked in by cameron, 15 months ago

More changes in preparation for LLVM 3.9, 4.0

File size: 10.2 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/IR/Module.h>
11#include <sys/file.h>
12#include <sys/stat.h>
13#include <fcntl.h>
14#include <boost/filesystem.hpp>
15#include <boost/range/iterator_range.hpp>
16#include <boost/container/flat_set.hpp>
17#if LLVM_VERSION_INTEGER < LLVM_4_0_0
18#include <llvm/Bitcode/ReaderWriter.h>
19#else
20#include <llvm/Bitcode/BitcodeReader.h>
21#include <llvm/Bitcode/BitcodeWriter.h>
22#endif
23#include <llvm/IR/Verifier.h>
24#include <ctime>
25
26using namespace llvm;
27
28//===----------------------------------------------------------------------===//
29// Object cache (based on tools/lli/lli.cpp, LLVM 3.6.1)
30//
31// This object cache implementation writes cached objects to disk to the
32// directory specified by CacheDir, using a filename provided in the module
33// descriptor. The cache tries to load a saved object using that path if the
34// file exists.
35//
36
37#define MONTH_1 \
38    ((__DATE__ [0] == 'O' || __DATE__ [0] == 'N' || __DATE__ [0] == 'D') ? '1' : '0')
39#define MONTH_2 \
40    (__DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? '1' : '6') \
41    : __DATE__ [2] == 'b' ? '2' \
42    : __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? '3' : '4') \
43    : __DATE__ [2] == 'y' ? '5' \
44    : __DATE__ [2] == 'l' ? '7' \
45    : __DATE__ [2] == 'g' ? '8' \
46    : __DATE__ [2] == 'p' ? '9' \
47    : __DATE__ [2] == 't' ? '0' \
48    : __DATE__ [2] == 'v' ? '1' : '2')
49#define DAY_1 (__DATE__[4] == ' ' ? '0' : __DATE__[4])
50#define DAY_2 (__DATE__[5])
51#define YEAR_1 (__DATE__[9])
52#define YEAR_2 (__DATE__[10])
53#define HOUR_1 (__TIME__[0])
54#define HOUR_2 (__TIME__[1])
55#define MINUTE_1 (__TIME__[3])
56#define MINUTE_2 (__TIME__[4])
57#define SECOND_1 (__TIME__[6])
58#define SECOND_2 (__TIME__[7])
59
60const static auto CACHE_PREFIX = PARABIX_VERSION +
61                          std::string{'@',
62                          MONTH_1, MONTH_2, DAY_1, DAY_2, YEAR_1, YEAR_2,
63                          HOUR_1, HOUR_2, MINUTE_1, MINUTE_2, SECOND_1, SECOND_2,
64                          '_'};
65
66const static auto CACHEABLE = "cacheable";
67
68const static auto SIGNATURE = "signature";
69
70const static boost::uintmax_t CACHE_SIZE_LIMIT = 50 * 1024 * 1024;
71
72const MDString * getSignature(const llvm::Module * const M) {
73    NamedMDNode * const sig = M->getNamedMetadata(SIGNATURE);
74    if (sig) {
75        assert ("empty metadata node" && sig->getNumOperands() == 1);
76        assert ("no signature payload" && sig->getOperand(0)->getNumOperands() == 1);
77        return cast<MDString>(sig->getOperand(0)->getOperand(0));
78    }
79    return nullptr;
80}
81
82bool ParabixObjectCache::loadCachedObjectFile(const std::unique_ptr<kernel::KernelBuilder> & idb, kernel::Kernel * const kernel) {
83    if (LLVM_LIKELY(kernel->isCachable())) {
84        assert (kernel->getModule() == nullptr);
85        const auto moduleId = kernel->getCacheName(idb);
86
87        // Have we already seen this module before?
88        const auto f = mCachedObject.find(moduleId);
89        if (LLVM_UNLIKELY(f != mCachedObject.end())) {
90            Module * const m = f->second.first; assert (m);
91            kernel->setModule(m);
92            kernel->prepareCachedKernel(idb);
93            return true;
94        }
95
96        // No, check for an existing cache file.
97        Path objectName(mCachePath);
98        sys::path::append(objectName, CACHE_PREFIX);
99        objectName.append(moduleId);
100        objectName.append(".o");
101
102        auto objectBuffer = MemoryBuffer::getFile(objectName.c_str(), -1, false);
103        if (objectBuffer) {
104            if (kernel->hasSignature()) {
105                sys::path::replace_extension(objectName, ".sig");
106                const auto signatureBuffer = MemoryBuffer::getFile(objectName.c_str(), -1, false);
107                if (signatureBuffer) {
108                    const StringRef loadedSig = signatureBuffer.get()->getBuffer();
109                    if (LLVM_UNLIKELY(!loadedSig.equals(kernel->makeSignature(idb)))) {
110                        goto invalid;
111                    }
112                } else {
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 object file
134                    sys::path::replace_extension(objectName, ".o");
135                    boost::filesystem::last_write_time(objectName.c_str(), time(0));
136                    return true;
137                }
138            }
139        }
140
141invalid:
142
143        Module * const module = kernel->setModule(new Module(moduleId, idb->getContext()));
144        // mark this module as cachable
145        module->getOrInsertNamedMetadata(CACHEABLE);
146        // if this module has a signature, add it to the metadata
147        if (kernel->hasSignature()) {
148            NamedMDNode * const md = module->getOrInsertNamedMetadata(SIGNATURE);
149            assert (md->getNumOperands() == 0);
150            MDString * const sig = MDString::get(module->getContext(), kernel->makeSignature(idb));
151            md->addOperand(MDNode::get(module->getContext(), {sig}));
152        }
153    }
154    return false;
155}
156
157// A new module has been compiled. If it is cacheable and no conflicting module
158// exists, write it out.
159void ParabixObjectCache::notifyObjectCompiled(const Module * M, MemoryBufferRef Obj) {
160    if (LLVM_LIKELY(M->getNamedMetadata(CACHEABLE))) {
161        const auto moduleId = M->getModuleIdentifier();
162        Path objectName(mCachePath);
163        sys::path::append(objectName, CACHE_PREFIX);
164        objectName.append(moduleId);
165        objectName.append(".o");
166
167        if (LLVM_LIKELY(!mCachePath.empty())) {
168            sys::fs::create_directories(Twine(mCachePath));
169        }
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        // then the signature (if one exists)
178        const MDString * const sig = getSignature(M);
179        if (sig) {
180            sys::path::replace_extension(objectName, ".sig");
181            raw_fd_ostream sigfile(objectName, EC, sys::fs::F_None);
182            sigfile << sig->getString();
183            sigfile.close();
184        }
185
186        // and finally kernel prototype header.
187        std::unique_ptr<Module> header(new Module(M->getModuleIdentifier(), M->getContext()));
188        for (const Function & f : M->getFunctionList()) {
189            if (f.hasExternalLinkage() && !f.empty()) {
190                Function::Create(f.getFunctionType(), Function::ExternalLinkage, f.getName(), header.get());
191            }
192        }
193
194        sys::path::replace_extension(objectName, ".kernel");
195        raw_fd_ostream kernelFile(objectName.str(), EC, sys::fs::F_None);
196        WriteBitcodeToFile(header.get(), kernelFile);
197        kernelFile.close();
198    }
199}
200
201void ParabixObjectCache::cleanUpObjectCacheFiles() {
202
203    using namespace boost::filesystem;
204    using ObjectFile = std::pair<std::time_t, path>;
205
206    path cachePath(mCachePath.str());
207    if (LLVM_LIKELY(is_directory(cachePath))) {
208        std::vector<ObjectFile> files;
209        for(const directory_entry & entry : boost::make_iterator_range(directory_iterator(cachePath), {})) {
210            const auto path = entry.path();;
211            if (LLVM_LIKELY(is_regular_file(path) && path.has_extension() && path.extension().compare(".o") == 0)) {
212                files.emplace_back(last_write_time(path), path.filename());
213            }
214        }
215        // sort the files in decending order of last modified (datetime) then file name
216        std::sort(files.begin(), files.end(), std::greater<ObjectFile>());
217        boost::uintmax_t cacheSize = 0;
218        for(const ObjectFile & entry : files) {
219            auto objectPath = cachePath / std::get<1>(entry);
220            if (LLVM_LIKELY(exists(objectPath))) {
221                const auto size = file_size(objectPath);
222                if ((cacheSize + size) < CACHE_SIZE_LIMIT) {
223                    cacheSize += size;
224                } else {
225                    remove(objectPath);
226                    objectPath.replace_extension("sig");
227                    remove(objectPath);
228                    objectPath.replace_extension("kernel");
229                    remove(objectPath);
230                }
231            }
232        }
233    }
234}
235
236std::unique_ptr<MemoryBuffer> ParabixObjectCache::getObject(const Module * module) {
237    const auto f = mCachedObject.find(module->getModuleIdentifier());
238    if (f == mCachedObject.end()) {
239        return nullptr;
240    }
241    // Return a copy of the buffer, for MCJIT to modify, if necessary.
242    return MemoryBuffer::getMemBufferCopy(f->second.second.get()->getBuffer());
243}
244
245inline ParabixObjectCache::Path ParabixObjectCache::getDefaultPath() {
246    // $HOME/.cache/parabix/
247    Path cachePath;
248#if LLVM_VERSION_INTEGER < LLVM_3_7_0
249    sys::path::user_cache_directory(cachePath, "parabix", PARABIX_VERSION);
250#else
251    sys::path::home_directory(cachePath);
252    sys::path::append(cachePath, ".cache", "parabix", PARABIX_VERSION);
253#endif
254    return cachePath;
255}
256
257ParabixObjectCache::ParabixObjectCache()
258: mCachePath(getDefaultPath()) {
259
260}
261
262ParabixObjectCache::ParabixObjectCache(const std::string dir)
263: mCachePath(dir) {
264
265}
Note: See TracBrowser for help on using the repository browser.