/ libevmjit / Cache.cpp
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  }