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

Last change on this file since 5544 was 5493, checked in by cameron, 2 years ago

Restore check-ins from the last several days

File size: 8.0 KB
Line 
1#include "object_cache.h"
2#include <kernels/kernel.h>
3#include <kernels/kernel_builder.h>
4#include <llvm/Support/raw_ostream.h>
5#include <llvm/Support/MemoryBuffer.h>
6#include <llvm/IR/Metadata.h>
7#include <llvm/Support/FileSystem.h>
8#include <llvm/Support/Path.h>
9#include <llvm/IR/Module.h>
10#include <sys/file.h>
11#include <sys/stat.h>
12#include <fcntl.h>
13#include <boost/filesystem.hpp>
14#include <boost/range/iterator_range.hpp>
15#include <ctime>
16
17using namespace llvm;
18
19//===----------------------------------------------------------------------===//
20// Object cache (based on tools/lli/lli.cpp, LLVM 3.6.1)
21//
22// This object cache implementation writes cached objects to disk to the
23// directory specified by CacheDir, using a filename provided in the module
24// descriptor. The cache tries to load a saved object using that path if the
25// file exists.
26//
27
28#define MONTH_1 \
29    ((__DATE__ [0] == 'O' || __DATE__ [0] == 'N' || __DATE__ [0] == 'D') ? '1' : '0')
30#define MONTH_2 \
31    (__DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? '1' : '6') \
32    : __DATE__ [2] == 'b' ? '2' \
33    : __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? '3' : '4') \
34    : __DATE__ [2] == 'y' ? '5' \
35    : __DATE__ [2] == 'l' ? '7' \
36    : __DATE__ [2] == 'g' ? '8' \
37    : __DATE__ [2] == 'p' ? '9' \
38    : __DATE__ [2] == 't' ? '0' \
39    : __DATE__ [2] == 'v' ? '1' : '2')
40#define DAY_1 (__DATE__[4] == ' ' ? '0' : __DATE__[4])
41#define DAY_2 (__DATE__[5])
42#define YEAR_1 (__DATE__[9])
43#define YEAR_2 (__DATE__[10])
44#define HOUR_1 (__TIME__[0])
45#define HOUR_2 (__TIME__[1])
46#define MINUTE_1 (__TIME__[3])
47#define MINUTE_2 (__TIME__[4])
48#define SECOND_1 (__TIME__[6])
49#define SECOND_2 (__TIME__[7])
50
51const static auto CACHE_PREFIX = PARABIX_VERSION +
52                          std::string{'@',
53                          MONTH_1, MONTH_2, DAY_1, DAY_2, YEAR_1, YEAR_2,
54                          HOUR_1, HOUR_2, MINUTE_1, MINUTE_2, SECOND_1, SECOND_2,
55                          '_'};
56
57const static auto CACHEABLE = "cacheable";
58
59const static auto SIGNATURE = "signature";
60
61const static boost::uintmax_t CACHE_SIZE_LIMIT = 5 * 1024 * 1024;
62
63const MDString * getSignature(const llvm::Module * const M) {
64    NamedMDNode * const sig = M->getNamedMetadata(SIGNATURE);
65    if (sig) {
66        assert ("empty metadata node" && sig->getNumOperands() == 1);
67        assert ("no signature payload" && sig->getOperand(0)->getNumOperands() == 1);
68        return cast<MDString>(sig->getOperand(0)->getOperand(0));
69    }
70    return nullptr;
71}
72
73bool ParabixObjectCache::loadCachedObjectFile(const std::unique_ptr<kernel::KernelBuilder> & idb, kernel::Kernel * const kernel) {
74    if (LLVM_LIKELY(kernel->isCachable())) {
75        Module * const module = kernel->getModule();
76        assert ("kernel module cannot be null!" && module);
77        const auto moduleId = module->getModuleIdentifier();
78        // Have we already seen this module before?
79        if (LLVM_UNLIKELY(mCachedObject.count(moduleId) != 0)) {
80            return true;
81        }
82
83        // No, check for an existing cache file.
84        Path objectName(mCachePath);
85        sys::path::append(objectName, CACHE_PREFIX);
86        objectName.append(moduleId);
87        objectName.append(".o");
88
89        auto objectBuffer = MemoryBuffer::getFile(objectName.c_str(), -1, false);
90        if (objectBuffer) {
91            if (kernel->hasSignature()) {
92                sys::path::replace_extension(objectName, ".sig");
93                const auto signatureBuffer = MemoryBuffer::getFile(objectName.c_str(), -1, false);
94                if (signatureBuffer) {
95                    const StringRef loadedSig = signatureBuffer.get()->getBuffer();
96                    if (!loadedSig.equals(kernel->makeSignature(idb))) {
97                        return false;
98                    }
99                } else {
100                    report_fatal_error("signature file expected but not found: " + moduleId);
101                    return false;
102                }
103            }
104            // update the modified time of the file then add it to our cache
105            boost::filesystem::last_write_time(objectName.c_str(), time(0));
106            mCachedObject.emplace(moduleId, std::move(objectBuffer.get()));
107            return true;
108        } else {
109            // mark this module as cachable
110            module->getOrInsertNamedMetadata(CACHEABLE);
111            // if this module has a signature, add it to the metadata
112            if (kernel->hasSignature()) {
113                NamedMDNode * const md = module->getOrInsertNamedMetadata(SIGNATURE);
114                assert (md->getNumOperands() == 0);
115                MDString * const sig = MDString::get(module->getContext(), kernel->makeSignature(idb));               
116                md->addOperand(MDNode::get(module->getContext(), {sig}));
117            }
118        }
119    }
120    return false;
121}
122
123// A new module has been compiled. If it is cacheable and no conflicting module
124// exists, write it out.
125void ParabixObjectCache::notifyObjectCompiled(const Module * M, MemoryBufferRef Obj) {
126    if (M->getNamedMetadata(CACHEABLE)) {
127        const auto moduleId = M->getModuleIdentifier();
128        Path objectName(mCachePath);
129        sys::path::append(objectName, CACHE_PREFIX);
130        objectName.append(moduleId);
131        objectName.append(".o");
132
133        if (LLVM_LIKELY(!mCachePath.empty())) {
134            sys::fs::create_directories(Twine(mCachePath));
135        }
136
137        std::error_code EC;
138        raw_fd_ostream outfile(objectName, EC, sys::fs::F_None);
139        outfile.write(Obj.getBufferStart(), Obj.getBufferSize());
140        outfile.close();
141
142        // If this module has a signature, write it.
143        const MDString * const sig = getSignature(M);
144        if (sig) {
145            sys::path::replace_extension(objectName, ".sig");
146            raw_fd_ostream sigfile(objectName, EC, sys::fs::F_None);
147            sigfile << sig->getString();
148            sigfile.close();
149        }
150    }
151}
152
153void ParabixObjectCache::cleanUpObjectCacheFiles() {
154
155    using namespace boost::filesystem;
156    using ObjectFile = std::pair<std::time_t, path>;
157
158    path cachePath(mCachePath.str());
159    if (LLVM_LIKELY(is_directory(cachePath))) {
160        std::vector<ObjectFile> files;
161        for(const directory_entry & entry : boost::make_iterator_range(directory_iterator(cachePath), {})) {
162            const auto path = entry.path();;
163            if (LLVM_LIKELY(is_regular_file(path) && path.has_extension() && path.extension().compare(".o") == 0)) {
164                files.emplace_back(last_write_time(path), path.filename());
165            }
166        }
167        // sort the files in decending order of last modified (datetime) then file name
168        std::sort(files.begin(), files.end(), std::greater<ObjectFile>());
169        boost::uintmax_t cacheSize = 0;
170        for(const ObjectFile & entry : files) {
171            auto objectPath = cachePath / std::get<1>(entry);
172            if (LLVM_LIKELY(exists(objectPath))) {
173                const auto size = file_size(objectPath);
174                if ((cacheSize + size) < CACHE_SIZE_LIMIT) {
175                    cacheSize += size;
176                } else {
177                    remove(objectPath);
178                    objectPath.replace_extension("sig");
179                    remove(objectPath);
180                }
181            }
182        }
183    }
184}
185
186std::unique_ptr<MemoryBuffer> ParabixObjectCache::getObject(const Module * module) {
187    const auto moduleId = module->getModuleIdentifier();
188    const auto f = mCachedObject.find(moduleId);
189    if (f == mCachedObject.end()) {
190        return nullptr;
191    }
192    // Return a copy of the buffer, for MCJIT to modify, if necessary.
193    return MemoryBuffer::getMemBufferCopy(f->second.get()->getBuffer());
194}
195
196inline ParabixObjectCache::Path ParabixObjectCache::getDefaultPath() {
197    // $HOME/.cache/parabix/
198    Path cachePath;
199    #ifndef USE_LLVM_3_6
200    sys::path::user_cache_directory(cachePath, "parabix");
201    #else
202    sys::path::home_directory(cachePath);
203    sys::path::append(cachePath, ".cache", "parabix");
204    #endif
205    return cachePath;
206}
207
208ParabixObjectCache::ParabixObjectCache()
209: mCachePath(getDefaultPath()) {
210
211}
212
213ParabixObjectCache::ParabixObjectCache(const std::string & dir)
214: mCachePath(dir) {
215
216}
Note: See TracBrowser for help on using the repository browser.