Cache.cpp
1 #include "Cache.h" 2 3 #include <mutex> 4 5 #include "preprocessor/llvm_includes_start.h" 6 #include <llvm/IR/Module.h> 7 #include <llvm/IR/LLVMContext.h> 8 #include <llvm/IR/Instructions.h> 9 #include <llvm/ExecutionEngine/ExecutionEngine.h> 10 #include <llvm/Support/Path.h> 11 #include <llvm/Support/FileSystem.h> 12 #include <llvm/Support/raw_os_ostream.h> 13 #include "preprocessor/llvm_includes_end.h" 14 15 #include "ExecStats.h" 16 #include "Utils.h" 17 18 namespace dev 19 { 20 namespace evmjit 21 { 22 23 namespace 24 { 25 /// The ABI version of jitted codes. It reflects how a generated code 26 /// communicates with outside world. When this communication changes old 27 /// cached code must be invalidated. 28 const auto c_internalABIVersion = 4; 29 30 using Guard = std::lock_guard<std::mutex>; 31 std::mutex x_cacheMutex; 32 CacheMode g_mode; 33 std::unique_ptr<llvm::MemoryBuffer> g_lastObject; 34 JITListener* g_listener; 35 36 std::string getVersionedCacheDir() 37 { 38 llvm::SmallString<256> path; 39 llvm::sys::path::user_cache_directory(path, "ethereum", "evmjit", 40 std::to_string(c_internalABIVersion)); 41 return path.str(); 42 } 43 44 } 45 46 ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener) 47 { 48 DLOG(cache) << "Cache dir: " << getVersionedCacheDir() << "\n"; 49 50 Guard g{x_cacheMutex}; 51 52 g_mode = _mode; 53 g_listener = _listener; 54 55 if (g_mode == CacheMode::clear) 56 { 57 Cache::clear(); 58 g_mode = CacheMode::off; 59 } 60 61 if (g_mode != CacheMode::off) 62 { 63 static ObjectCache objectCache; 64 return &objectCache; 65 } 66 return nullptr; 67 } 68 69 void Cache::clear() 70 { 71 Guard g{x_cacheMutex}; 72 73 auto cachePath = getVersionedCacheDir(); 74 std::error_code err; 75 for (auto it = llvm::sys::fs::directory_iterator{cachePath, err}; it != decltype(it){}; it.increment(err)) 76 llvm::sys::fs::remove(it->path()); 77 } 78 79 void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache, 80 llvm::LLVMContext& _llvmContext) 81 { 82 Guard g{x_cacheMutex}; 83 84 // Disable listener 85 auto listener = g_listener; 86 g_listener = nullptr; 87 88 auto cachePath = getVersionedCacheDir(); 89 std::error_code err; 90 for (auto it = llvm::sys::fs::directory_iterator{cachePath, err}; it != decltype(it){}; it.increment(err)) 91 { 92 auto name = it->path().substr(cachePath.size() + 1); 93 if (auto module = getObject(name, _llvmContext)) 94 { 95 DLOG(cache) << "Preload: " << name << "\n"; 96 _ee.addModule(std::move(module)); 97 auto addr = _ee.getFunctionAddress(name); 98 assert(addr); 99 _funcCache[std::move(name)] = addr; 100 } 101 } 102 103 g_listener = listener; 104 } 105 106 std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id, llvm::LLVMContext& _llvmContext) 107 { 108 Guard g{x_cacheMutex}; 109 110 if (g_mode != CacheMode::on && g_mode != CacheMode::read) 111 return nullptr; 112 113 // TODO: Disabled because is not thread-safe. 114 //if (g_listener) 115 // g_listener->stateChanged(ExecState::CacheLoad); 116 117 DLOG(cache) << id << ": search\n"; 118 if (!CHECK(!g_lastObject)) 119 g_lastObject = nullptr; 120 121 llvm::SmallString<256> cachePath{getVersionedCacheDir()}; 122 llvm::sys::path::append(cachePath, id); 123 124 if (auto r = llvm::MemoryBuffer::getFile(cachePath, -1, false)) 125 g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); 126 else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) 127 DLOG(cache) << r.getError().message(); // TODO: Add warning log 128 129 if (g_lastObject) // if object found create fake module 130 { 131 DLOG(cache) << id << ": found\n"; 132 auto module = llvm::make_unique<llvm::Module>(id, _llvmContext); 133 auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(_llvmContext), {}, false); 134 auto mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); 135 auto bb = llvm::BasicBlock::Create(_llvmContext, {}, mainFunc); 136 bb->getInstList().push_back(new llvm::UnreachableInst{_llvmContext}); 137 return module; 138 } 139 DLOG(cache) << id << ": not found\n"; 140 return nullptr; 141 } 142 143 144 void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) 145 { 146 Guard g{x_cacheMutex}; 147 148 // Only in "on" and "write" mode 149 if (g_mode != CacheMode::on && g_mode != CacheMode::write) 150 return; 151 152 // TODO: Disabled because is not thread-safe. 153 // if (g_listener) 154 // g_listener->stateChanged(ExecState::CacheWrite); 155 156 auto&& id = _module->getModuleIdentifier(); 157 llvm::SmallString<256> cachePath{getVersionedCacheDir()}; 158 if (auto err = llvm::sys::fs::create_directories(cachePath)) 159 { 160 DLOG(cache) << "Cannot create cache dir " << cachePath.str().str() << " (error: " << err.message() << "\n"; 161 return; 162 } 163 164 llvm::sys::path::append(cachePath, id); 165 166 DLOG(cache) << id << ": write\n"; 167 std::error_code error; 168 llvm::raw_fd_ostream cacheFile(cachePath, error, llvm::sys::fs::F_None); 169 cacheFile << _object.getBuffer(); 170 } 171 172 std::unique_ptr<llvm::MemoryBuffer> ObjectCache::getObject(llvm::Module const* _module) 173 { 174 Guard g{x_cacheMutex}; 175 176 DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; 177 return std::move(g_lastObject); 178 } 179 180 } 181 }