diff --git a/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h index 82be4a0c8..69f2788a4 100644 --- a/evmjit/libevmjit/Runtime.h +++ b/evmjit/libevmjit/Runtime.h @@ -8,8 +8,6 @@ namespace eth { namespace jit { - -using StackImpl = std::vector; using MemoryImpl = bytes; class Runtime @@ -20,7 +18,6 @@ public: Runtime(const Runtime&) = delete; Runtime& operator=(const Runtime&) = delete; - StackImpl& getStack() { return m_stack; } MemoryImpl& getMemory() { return m_memory; } bytes_ref getReturnData() const; @@ -31,7 +28,6 @@ private: void* m_currJmpBuf = nullptr; ///< Pointer to jump buffer. Expected by compiled contract. byte* m_memoryData = nullptr; i256 m_memorySize; - StackImpl m_stack; MemoryImpl m_memory; }; diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index 0b5762fa2..dd053fd90 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -93,7 +93,7 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf // save jmpBuf to be used in helper functions auto ptr = m_builder.CreateStructGEP(getRuntimePtr(), 2); - m_builder.CreateStore(m_jmpBuf, ptr, "jmpBufExt"); + m_builder.CreateStore(m_jmpBuf, ptr); // Unpack data auto rtPtr = getRuntimePtr(); diff --git a/evmjit/libevmjit/Stack.cpp b/evmjit/libevmjit/Stack.cpp index 4f09661f8..fc3b301bb 100644 --- a/evmjit/libevmjit/Stack.cpp +++ b/evmjit/libevmjit/Stack.cpp @@ -7,6 +7,8 @@ #include "RuntimeManager.h" #include "Runtime.h" +#include + namespace dev { namespace eth @@ -14,9 +16,151 @@ namespace eth namespace jit { +static const auto c_reallocStep = 1; +static const auto c_reallocMultipier = 2; + +llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list const& _args) +{ + if (!m_func) + m_func = m_creator(); + + return _builder.CreateCall(m_func, {_args.begin(), _args.size()}); +} + +llvm::Function* Array::createArrayPushFunc() +{ + llvm::Type* argTypes[] = {m_array->getType(), Type::Word}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.push", getModule()); + func->setDoesNotThrow(); + func->setDoesNotCapture(1); + + llvm::Type* reallocArgTypes[] = {Type::BytePtr, Type::Size}; + auto reallocFunc = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, reallocArgTypes, false), llvm::Function::ExternalLinkage, "realloc", getModule()); + reallocFunc->setDoesNotThrow(); + reallocFunc->setDoesNotAlias(0); + reallocFunc->setDoesNotCapture(1); + + auto arrayPtr = &func->getArgumentList().front(); + arrayPtr->setName("arrayPtr"); + auto value = arrayPtr->getNextNode(); + value->setName("value"); + + InsertPointGuard guard{m_builder}; + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func); + auto reallocBB = llvm::BasicBlock::Create(m_builder.getContext(), "Realloc", func); + auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func); + + m_builder.SetInsertPoint(entryBB); + auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); + auto sizePtr = m_builder.CreateStructGEP(arrayPtr, 1, "sizePtr"); + auto capPtr = m_builder.CreateStructGEP(arrayPtr, 2, "capPtr"); + auto data = m_builder.CreateLoad(dataPtr, "data"); + auto size = m_builder.CreateLoad(sizePtr, "size"); + auto cap = m_builder.CreateLoad(capPtr, "cap"); + auto reallocReq = m_builder.CreateICmpEQ(cap, size, "reallocReq"); + m_builder.CreateCondBr(reallocReq, reallocBB, pushBB); + + m_builder.SetInsertPoint(reallocBB); + auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap"); + //newCap = m_builder.CreateNUWMul(newCap, m_builder.getInt64(c_reallocMultipier)); + auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32 + auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes"); + auto newBytes = m_builder.CreateCall2(reallocFunc, bytes, reallocSize, "newBytes"); + auto newData = m_builder.CreateBitCast(newBytes, Type::WordPtr, "newData"); + m_builder.CreateStore(newData, dataPtr); + m_builder.CreateStore(newCap, capPtr); + m_builder.CreateBr(pushBB); + + m_builder.SetInsertPoint(pushBB); + auto dataPhi = m_builder.CreatePHI(Type::WordPtr, 2, "dataPhi"); + dataPhi->addIncoming(data, entryBB); + dataPhi->addIncoming(newData, reallocBB); + auto newElemPtr = m_builder.CreateGEP(dataPhi, size, "newElemPtr"); + m_builder.CreateStore(value, newElemPtr); + auto newSize = m_builder.CreateNUWAdd(size, m_builder.getInt64(1), "newSize"); + m_builder.CreateStore(newSize, sizePtr); + m_builder.CreateRetVoid(); + + return func; +} + +llvm::Function* Array::createArraySetFunc() +{ + llvm::Type* argTypes[] = {m_array->getType(), Type::Size, Type::Word}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.set", getModule()); + func->setDoesNotThrow(); + func->setDoesNotCapture(1); + + auto arrayPtr = &func->getArgumentList().front(); + arrayPtr->setName("arrayPtr"); + auto index = arrayPtr->getNextNode(); + index->setName("index"); + auto value = index->getNextNode(); + value->setName("value"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); + auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); + auto data = m_builder.CreateLoad(dataPtr, "data"); + auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); + m_builder.CreateStore(value, valuePtr); + m_builder.CreateRetVoid(); + return func; +} + +llvm::Function* Array::createArrayGetFunc() +{ + llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "array.get", getModule()); + func->setDoesNotThrow(); + func->setDoesNotCapture(1); + + auto arrayPtr = &func->getArgumentList().front(); + arrayPtr->setName("arrayPtr"); + auto index = arrayPtr->getNextNode(); + index->setName("index"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); + auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); + auto data = m_builder.CreateLoad(dataPtr, "data"); + auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); + auto value = m_builder.CreateLoad(valuePtr, "value"); + m_builder.CreateRet(value); + return func; +} + +Array::Array(llvm::IRBuilder<>& _builder, char const* _name) : + CompilerHelper(_builder), + m_pushFunc([this](){ return createArrayPushFunc(); }), + m_setFunc([this](){ return createArraySetFunc(); }), + m_getFunc([this](){ return createArrayGetFunc(); }) +{ + llvm::Type* elementTys[] = {Type::WordPtr, Type::Size, Type::Size}; + static auto arrayTy = llvm::StructType::create(elementTys, "Array"); + + m_array = m_builder.CreateAlloca(arrayTy, nullptr, _name); + m_builder.CreateStore(llvm::ConstantAggregateZero::get(arrayTy), m_array); +} + +void Array::pop(llvm::Value* _count) +{ + auto sizePtr = m_builder.CreateStructGEP(m_array, 1, "sizePtr"); + auto size = m_builder.CreateLoad(sizePtr, "size"); + auto newSize = m_builder.CreateNUWSub(size, _count, "newSize"); + m_builder.CreateStore(newSize, sizePtr); +} + +llvm::Value* Array::size() +{ + auto sizePtr = m_builder.CreateStructGEP(m_array, 1, "sizePtr"); + return m_builder.CreateLoad(sizePtr, "array.size"); +} + Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager): CompilerHelper(_builder), - m_runtimeManager(_runtimeManager) + m_runtimeManager(_runtimeManager), + m_stack(_builder, "stack") {} llvm::Function* Stack::getPushFunc() @@ -53,7 +197,7 @@ llvm::Function* Stack::getSetFunc() llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::Word}; func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.set", getModule()); llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr}; - auto extPushFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_set", getModule()); + auto extSetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_set", getModule()); auto rt = &func->getArgumentList().front(); rt->setName("rt"); @@ -67,7 +211,7 @@ llvm::Function* Stack::getSetFunc() m_builder.SetInsertPoint(entryBB); auto a = m_builder.CreateAlloca(Type::Word); m_builder.CreateStore(value, a); - createCall(extPushFunc, {rt, index, a}); + createCall(extSetFunc, {rt, index, a}); m_builder.CreateRetVoid(); } return func; @@ -114,16 +258,14 @@ llvm::Function* Stack::getGetFunc() auto& func = m_get; if (!func) { - llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr}; - func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::ExternalLinkage, "stack.get", getModule()); - llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size}; - auto extGetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_get", getModule()); + llvm::Type* argTypes[] = {Type::Size, Type::Size, Type::BytePtr}; + func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.require", getModule()); - auto rt = &func->getArgumentList().front(); - rt->setName("rt"); - auto index = rt->getNextNode(); + auto index = &func->getArgumentList().front(); index->setName("index"); - auto jmpBuf = index->getNextNode(); + auto size = index->getNextNode(); + size->setName("size"); + auto jmpBuf = size->getNextNode(); jmpBuf->setName("jmpBuf"); InsertPointGuard guard{m_builder}; @@ -132,39 +274,45 @@ llvm::Function* Stack::getGetFunc() auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); m_builder.SetInsertPoint(entryBB); - auto valuePtr = createCall(extGetFunc, {rt, index}); - auto ok = m_builder.CreateICmpNE(valuePtr, llvm::ConstantPointerNull::get(Type::WordPtr)); - m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight + auto underflow = m_builder.CreateICmpUGE(index, size, "underflow"); + m_builder.CreateCondBr(underflow, underflowBB, returnBB); m_builder.SetInsertPoint(underflowBB); m_runtimeManager.abort(jmpBuf); m_builder.CreateUnreachable(); m_builder.SetInsertPoint(returnBB); - m_builder.CreateRet(valuePtr); + m_builder.CreateRetVoid(); } return func; } llvm::Value* Stack::get(size_t _index) { - auto valuePtr = createCall(getGetFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_index), m_runtimeManager.getJmpBuf()}); - return m_builder.CreateLoad(valuePtr); + createCall(getGetFunc(), {m_builder.getInt64(_index), m_stack.size(), m_runtimeManager.getJmpBuf()}); + auto value = m_stack.get(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1))); + //return m_builder.CreateLoad(valuePtr); + return value; } void Stack::set(size_t _index, llvm::Value* _value) { - createCall(getSetFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_index), _value}); + m_stack.set(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1)), _value); + //createCall(getSetFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_index), _value}); } void Stack::pop(size_t _count) { - createCall(getPopFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_count), m_runtimeManager.getJmpBuf()}); + // FIXME: Pop does not check for stack underflow but looks like not needed + // We should place stack.require() check and begining of every BB + m_stack.pop(m_builder.getInt64(_count)); + //createCall(getPopFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_count), m_runtimeManager.getJmpBuf()}); } void Stack::push(llvm::Value* _value) { - createCall(getPushFunc(), {m_runtimeManager.getRuntimePtr(), _value}); + m_stack.push(_value); + //createCall(getPushFunc(), {m_runtimeManager.getRuntimePtr(), _value}); } } @@ -175,38 +323,6 @@ extern "C" { using namespace dev::eth::jit; - EXPORT bool stack_pop(Runtime* _rt, uint64_t _count) - { - auto& stack = _rt->getStack(); - if (stack.size() < _count) - return false; - - stack.erase(stack.end() - _count, stack.end()); - return true; - } - - EXPORT void stack_push(Runtime* _rt, i256 const* _word) - { - auto& stack = _rt->getStack(); - stack.push_back(*_word); - } - - EXPORT i256* stack_get(Runtime* _rt, uint64_t _index) - { - auto& stack = _rt->getStack(); - return _index < stack.size() ? &*(stack.rbegin() + _index) : nullptr; - } - - EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word) - { - auto& stack = _rt->getStack(); - assert(_index < stack.size()); - if (_index >= stack.size()) - return; - - *(stack.rbegin() + _index) = *_word; - } - EXPORT void ext_calldataload(RuntimeData* _rtData, i256* _index, byte* o_value) { // It asumes all indexes are less than 2^64 @@ -229,5 +345,13 @@ extern "C" } } + EXPORT void* ext_realloc(void* _data, size_t _size) + { + //std::cerr << "REALLOC: " << _data << " [" << _size << "]" << std::endl; + auto newData = std::realloc(_data, _size); + //std::cerr << "REALLOC: " << _data << " -> " << newData << " [" << _size << "]" << std::endl; + return newData; + } + } // extern "C" diff --git a/evmjit/libevmjit/Stack.h b/evmjit/libevmjit/Stack.h index e4bb33eae..242063962 100644 --- a/evmjit/libevmjit/Stack.h +++ b/evmjit/libevmjit/Stack.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "CompilerHelper.h" namespace dev @@ -10,6 +12,47 @@ namespace jit { class RuntimeManager; +class LazyFunction +{ +public: + using Creator = std::function; + + LazyFunction(Creator _creator) : + m_creator(_creator) + {} + + llvm::Value* call(llvm::IRBuilder<>& _builder, std::initializer_list const& _args); + +private: + llvm::Function* m_func = nullptr; + Creator m_creator; +}; + +class Array : public CompilerHelper +{ +public: + Array(llvm::IRBuilder<>& _builder, char const* _name); + + void push(llvm::Value* _value) { m_pushFunc.call(m_builder, {m_array, _value}); } + void set(llvm::Value* _index, llvm::Value* _value) { m_setFunc.call(m_builder, {m_array, _index, _value}); } + llvm::Value* get(llvm::Value* _index) { return m_getFunc.call(m_builder, {m_array, _index}); } + void pop(llvm::Value* _count); + llvm::Value* size(); + + llvm::Value* getPointerTo() const { return m_array; } + +private: + llvm::Value* m_array = nullptr; + + LazyFunction m_pushFunc; + LazyFunction m_setFunc; + LazyFunction m_getFunc; + + llvm::Function* createArrayPushFunc(); + llvm::Function* createArraySetFunc(); + llvm::Function* createArrayGetFunc(); +}; + class Stack : public CompilerHelper { public: @@ -32,6 +75,8 @@ private: llvm::Function* m_push = nullptr; llvm::Function* m_get = nullptr; llvm::Function* m_set = nullptr; + + Array m_stack; };