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

Last change on this file since 5636 was 5630, checked in by nmedfort, 2 years ago

Partial check-in for avoidance of compiling Pablo/LLVM code to determine the Kernel struct type when using a cached object. Inactive RE alternation minimization check in.

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