diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp
index 2d1dd0481..5b5ce420a 100644
--- a/alethzero/OurWebThreeStubServer.cpp
+++ b/alethzero/OurWebThreeStubServer.cpp
@@ -51,22 +51,24 @@ bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string
return button == QMessageBox::Ok;
}
-bool OurWebThreeStubServer::showCreationNotice(TransactionSkeleton const& _t) const
+bool OurWebThreeStubServer::showCreationNotice(TransactionSkeleton const& _t, bool _toProxy) const
{
- return showAuthenticationPopup("Contract Creation Transaction", "ÐApp is attemping to create a contract; to be endowed with " + formatBalance(_t.value) + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
+ return showAuthenticationPopup("Contract Creation Transaction", string("ÐApp is attemping to create a contract; ") + (_toProxy ? "(this transaction is not executed directly, but forwarded to another ÐApp) " : "") + "to be endowed with " + formatBalance(_t.value) + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
}
-bool OurWebThreeStubServer::showSendNotice(TransactionSkeleton const& _t) const
+bool OurWebThreeStubServer::showSendNotice(TransactionSkeleton const& _t, bool _toProxy) const
{
- return showAuthenticationPopup("Fund Transfer Transaction", "ÐApp is attempting to send " + formatBalance(_t.value) + " to a recipient " + m_main->pretty(_t.to).toStdString() + ", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
+ return showAuthenticationPopup("Fund Transfer Transaction", "ÐApp is attempting to send " + formatBalance(_t.value) + " to a recipient " + m_main->pretty(_t.to).toStdString() + (_toProxy ? " (this transaction is not executed directly, but forwarded to another ÐApp)" : "") +
+", with additional network fees of up to " + formatBalance(_t.gas * _t.gasPrice) + ".\n\nMaximum total cost is " + formatBalance(_t.value + _t.gas * _t.gasPrice) + ".");
}
-bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t) const
+bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t, bool _toProxy) const
{
return showAuthenticationPopup("DANGEROUS! Unknown Contract Transaction!",
"ÐApp is attempting to call into an unknown contract at address " +
- m_main->pretty(_t.to).toStdString() +
- ".\n\nCall involves sending " +
+ m_main->pretty(_t.to).toStdString() + ".\n\n" +
+ (_toProxy ? "This transaction is not executed directly, but forwarded to another ÐApp.\n\n" : "") +
+ "Call involves sending " +
formatBalance(_t.value) + " to the recipient, with additional network fees of up to " +
formatBalance(_t.gas * _t.gasPrice) +
"However, this also does other stuff which we don't understand, and does so in your name.\n\n" +
@@ -76,25 +78,25 @@ bool OurWebThreeStubServer::showUnknownCallNotice(TransactionSkeleton const& _t)
"REJECT UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!");
}
-bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t)
+bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t, bool _toProxy)
{
if (_t.creation)
{
// recipient has no code - nothing special about this transaction, show basic value transfer info
- return showCreationNotice(_t);
+ return showCreationNotice(_t, _toProxy);
}
h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to);
if (contractCodeHash == EmptySHA3)
{
// recipient has no code - nothing special about this transaction, show basic value transfer info
- return showSendNotice(_t);
+ return showSendNotice(_t, _toProxy);
}
string userNotice = m_main->natSpec()->getUserNotice(contractCodeHash, _t.data);
if (userNotice.empty())
- return showUnknownCallNotice(_t);
+ return showUnknownCallNotice(_t, _toProxy);
NatspecExpressionEvaluator evaluator;
userNotice = evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString();
@@ -104,6 +106,7 @@ bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t)
"ÐApp attempting to conduct contract interaction with " +
m_main->pretty(_t.to).toStdString() +
": " + userNotice + ".\n\n" +
+ (_toProxy ? "This transaction is not executed directly, but forwarded to another ÐApp.\n\n" : "") +
(_t.value > 0 ?
"In addition, ÐApp is attempting to send " +
formatBalance(_t.value) + " to said recipient, with additional network fees of up to " +
diff --git a/alethzero/OurWebThreeStubServer.h b/alethzero/OurWebThreeStubServer.h
index fbdae7d03..1ab5f813c 100644
--- a/alethzero/OurWebThreeStubServer.h
+++ b/alethzero/OurWebThreeStubServer.h
@@ -35,16 +35,16 @@ public:
std::vector const& _accounts, Main* main);
virtual std::string shh_newIdentity() override;
- virtual bool authenticate(dev::eth::TransactionSkeleton const& _t);
+ virtual bool authenticate(dev::eth::TransactionSkeleton const& _t, bool _toProxy);
signals:
void onNewId(QString _s);
private:
bool showAuthenticationPopup(std::string const& _title, std::string const& _text) const;
- bool showCreationNotice(dev::eth::TransactionSkeleton const& _t) const;
- bool showSendNotice(dev::eth::TransactionSkeleton const& _t) const;
- bool showUnknownCallNotice(dev::eth::TransactionSkeleton const& _t) const;
+ bool showCreationNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy) const;
+ bool showSendNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy) const;
+ bool showUnknownCallNotice(dev::eth::TransactionSkeleton const& _t, bool _toProxy) const;
dev::WebThreeDirect* m_web3;
Main* m_main;
diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt
index a449f9a60..14fca2cde 100644
--- a/evmjit/CMakeLists.txt
+++ b/evmjit/CMakeLists.txt
@@ -3,13 +3,14 @@ cmake_minimum_required(VERSION 2.8.12)
project(evmjit)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+set(CMAKE_AUTOMOC OFF)
-if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else()
- set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC")
+ set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-unknown-pragmas")
endif()
-if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
# Do not allow unresovled symbols in shared library (default on linux)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
endif()
diff --git a/evmjit/LICENSE.md b/evmjit/LICENSE.md
new file mode 100644
index 000000000..630157f98
--- /dev/null
+++ b/evmjit/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Paweł Bylica
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/evmjit/README.md b/evmjit/README.md
new file mode 100644
index 000000000..a480e83dc
--- /dev/null
+++ b/evmjit/README.md
@@ -0,0 +1,43 @@
+# The Ethereum EVM JIT
+
+EVM JIT is a library for just-in-time compilation of Ethereum EVM code.
+It can be used to substitute classic interpreter-like EVM Virtual Machine in Ethereum client.
+
+## Build
+
+### Linux / Ubuntu
+
+1. Install llvm-3.5-dev package
+ 1. For Ubuntu 14.04 using LLVM deb packages source: http://llvm.org/apt
+ 2. For Ubuntu 14.10 using Ubuntu packages
+2. Build library with cmake
+ 1. `mkdir build && cd $_`
+ 2. `cmake .. && make`
+3. Install library
+ 1. `sudo make install`
+ 2. `sudo ldconfig`
+
+### OSX
+
+1. Install llvm35
+ 1. `brew install llvm35 --disable-shared --HEAD`
+2. Build library with cmake
+ 1. `mkdir build && cd $_`
+ 2. `cmake -DLLVM_DIR=/usr/local/lib/llvm-3.5/share/llvm/cmake .. && make`
+3. Install library
+ 1. `make install` (with admin rights?)
+
+### Windows
+
+Ask me.
+
+## Options
+
+Options to evmjit library can be passed by environmental variables, e.g. `EVMJIT_CACHE=0 testeth --jit`.
+
+Option | Default value | Description
+------------- | ------------- | ----------------------------------------------
+EVMJIT_CACHE | 1 | Enables on disk cache for compiled EVM objects
+EVMJIT_DUMP | 0 | Dumps generated LLVM module to standard output
+
+
diff --git a/evmjit/libevmjit-cpp/CMakeLists.txt b/evmjit/libevmjit-cpp/CMakeLists.txt
index 58375e4ee..53448332b 100644
--- a/evmjit/libevmjit-cpp/CMakeLists.txt
+++ b/evmjit/libevmjit-cpp/CMakeLists.txt
@@ -9,6 +9,11 @@ set(SOURCES
)
source_group("" FILES ${SOURCES})
+if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
+else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive
+endif()
+
add_library(${TARGET_NAME} ${SOURCES})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")
diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp
index 228f637e8..11882d79d 100644
--- a/evmjit/libevmjit-cpp/Env.cpp
+++ b/evmjit/libevmjit-cpp/Env.cpp
@@ -1,4 +1,5 @@
+#pragma GCC diagnostic ignored "-Wconversion"
#include
#include
#include
@@ -46,23 +47,22 @@ extern "C"
*o_hash = _env->blockhash(llvm2eth(*_number));
}
- EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
+ EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
{
auto endowment = llvm2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
_env->subBalance(endowment);
- auto gas = llvm2eth(*io_gas);
- OnOpFunc onOp {}; // TODO: Handle that thing
- h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, onOp), h256::AlignRight);
- *io_gas = eth2llvm(gas);
+ u256 gas = *io_gas;
+ h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight);
+ *io_gas = static_cast(gas);
*o_address = address;
}
else
*o_address = {};
}
- EXPORT bool env_call(ExtVMFace* _env, i256* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
+ EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{
auto value = llvm2eth(*_value);
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
@@ -71,11 +71,10 @@ extern "C"
auto receiveAddress = right160(*_receiveAddress);
auto inRef = bytesConstRef{_inBeg, _inSize};
auto outRef = bytesRef{_outBeg, _outSize};
- OnOpFunc onOp {}; // TODO: Handle that thing
auto codeAddress = right160(*_codeAddress);
- auto gas = llvm2eth(*io_gas);
- auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, onOp, {}, codeAddress);
- *io_gas = eth2llvm(gas);
+ u256 gas = *io_gas;
+ auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, {}, {}, codeAddress);
+ *io_gas = static_cast(gas);
return ret;
}
diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp
index 413525e5a..55dcd94f8 100644
--- a/evmjit/libevmjit-cpp/JitVM.cpp
+++ b/evmjit/libevmjit-cpp/JitVM.cpp
@@ -1,6 +1,9 @@
+#pragma GCC diagnostic ignored "-Wconversion"
#include "JitVM.h"
#include
+#include
+#include
#include
#include "Utils.h"
@@ -11,22 +14,26 @@ namespace eth
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
-bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
+bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
{
using namespace jit;
- if (m_gas > std::numeric_limits::max())
- BOOST_THROW_EXCEPTION(OutOfGas()); // Do not accept requests with gas > 2^63 (int64 max) // TODO: Return "not accepted" exception to allow interpreted handle that
-
- if (_ext.gasPrice > std::numeric_limits::max())
- BOOST_THROW_EXCEPTION(OutOfGas());
-
- if (_ext.currentBlock.number > std::numeric_limits::max())
- BOOST_THROW_EXCEPTION(OutOfGas());
-
- if (_ext.currentBlock.timestamp > std::numeric_limits::max())
- BOOST_THROW_EXCEPTION(OutOfGas());
+ auto rejected = false;
+ // TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
+ rejected |= m_gas > std::numeric_limits::max(); // Do not accept requests with gas > 2^63 (int64 max)
+ rejected |= _ext.gasPrice > std::numeric_limits::max();
+ rejected |= _ext.currentBlock.number > std::numeric_limits::max();
+ rejected |= _ext.currentBlock.timestamp > std::numeric_limits::max();
+ if (rejected)
+ {
+ UNTESTED;
+ std::cerr << "Rejected\n";
+ VMFactory::setKind(VMKind::Interpreter);
+ m_fallbackVM = VMFactory::create(m_gas);
+ VMFactory::setKind(VMKind::JIT);
+ return m_fallbackVM->go(_ext, _onOp, _step);
+ }
m_data.gas = static_cast(m_gas);
m_data.gasPrice = static_cast(_ext.gasPrice);
@@ -43,9 +50,10 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
m_data.timestamp = static_cast(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size();
+ m_data.codeHash = eth2llvm(sha3(_ext.code));
auto env = reinterpret_cast(&_ext);
- auto exitCode = m_engine.run(_ext.code, &m_data, env);
+ auto exitCode = m_engine.run(&m_data, env);
switch (exitCode)
{
case ReturnCode::Suicide:
diff --git a/evmjit/libevmjit-cpp/JitVM.h b/evmjit/libevmjit-cpp/JitVM.h
index 90855127e..58caa3648 100644
--- a/evmjit/libevmjit-cpp/JitVM.h
+++ b/evmjit/libevmjit-cpp/JitVM.h
@@ -12,15 +12,13 @@ class JitVM: public VMFace
{
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
- enum Kind: bool { Interpreter, JIT };
- static std::unique_ptr create(Kind, u256 _gas = 0);
-
private:
friend class VMFactory;
explicit JitVM(u256 _gas = 0) : VMFace(_gas) {}
jit::RuntimeData m_data;
jit::ExecutionEngine m_engine;
+ std::unique_ptr m_fallbackVM; ///< VM used in case of input data rejected by JIT
};
diff --git a/evmjit/libevmjit/Arith256.cpp b/evmjit/libevmjit/Arith256.cpp
index 99ca63ea6..ddf06b463 100644
--- a/evmjit/libevmjit/Arith256.cpp
+++ b/evmjit/libevmjit/Arith256.cpp
@@ -1,11 +1,14 @@
#include "Arith256.h"
-#include "Runtime.h"
-#include "Type.h"
-#include "Endianness.h"
-#include
-#include
#include
+#include
+
+#include "preprocessor/llvm_includes_start.h"
+#include
+#include "preprocessor/llvm_includes_end.h"
+
+#include "Type.h"
+#include "Endianness.h"
namespace dev
{
@@ -16,29 +19,102 @@ namespace jit
Arith256::Arith256(llvm::IRBuilder<>& _builder) :
CompilerHelper(_builder)
-{
- using namespace llvm;
+{}
- m_result = m_builder.CreateAlloca(Type::Word, nullptr, "arith.result");
- m_arg1 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg1");
- m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg2");
- m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg3");
+void Arith256::debug(llvm::Value* _value, char _c)
+{
+ if (!m_debug)
+ {
+ llvm::Type* argTypes[] = {Type::Word, m_builder.getInt8Ty()};
+ m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule());
+ }
+ createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)});
+}
- using Linkage = GlobalValue::LinkageTypes;
+llvm::Function* Arith256::getMulFunc()
+{
+ auto& func = m_mul;
+ if (!func)
+ {
+ llvm::Type* argTypes[] = {Type::Word, Type::Word};
+ func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule());
- llvm::Type* arg2Types[] = {Type::WordPtr, Type::WordPtr, Type::WordPtr};
+ auto x = &func->getArgumentList().front();
+ x->setName("x");
+ auto y = x->getNextNode();
+ y->setName("y");
- m_mul = Function::Create(FunctionType::get(Type::Void, arg2Types, false), Linkage::ExternalLinkage, "arith_mul", getModule());
+ InsertPointGuard guard{m_builder};
+ auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
+ m_builder.SetInsertPoint(bb);
+ auto i64 = Type::Size;
+ auto i128 = m_builder.getIntNTy(128);
+ auto i256 = Type::Word;
+ auto x_lo = m_builder.CreateTrunc(x, i64, "x.lo");
+ auto y_lo = m_builder.CreateTrunc(y, i64, "y.lo");
+ auto x_mi = m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(64)), i64);
+ auto y_mi = m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(64)), i64);
+ auto x_hi = m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128);
+ auto y_hi = m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128);
+
+ auto t1 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_lo, i128));
+ auto t2 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_mi, i128));
+ auto t3 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), y_hi);
+ auto t4 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_lo, i128));
+ auto t5 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_mi, i128));
+ auto t6 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), y_hi);
+ auto t7 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_lo, i128));
+ auto t8 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_mi, i128));
+
+ auto p = m_builder.CreateZExt(t1, i256);
+ p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i256), Constant::get(64)));
+ p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i256), Constant::get(128)));
+ p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i256), Constant::get(64)));
+ p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t5, i256), Constant::get(128)));
+ p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t6, i256), Constant::get(192)));
+ p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t7, i256), Constant::get(128)));
+ p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t8, i256), Constant::get(192)));
+ m_builder.CreateRet(p);
+ }
+ return func;
}
-void Arith256::debug(llvm::Value* _value, char _c)
+llvm::Function* Arith256::getMul512Func()
{
- if (!m_debug)
+ auto& func = m_mul512;
+ if (!func)
{
- llvm::Type* argTypes[] = {Type::Word, m_builder.getInt8Ty()};
- m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule());
+ auto i512 = m_builder.getIntNTy(512);
+ llvm::Type* argTypes[] = {Type::Word, Type::Word};
+ func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule());
+
+ auto x = &func->getArgumentList().front();
+ x->setName("x");
+ auto y = x->getNextNode();
+ y->setName("y");
+
+ InsertPointGuard guard{m_builder};
+ auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
+ m_builder.SetInsertPoint(bb);
+ auto i128 = m_builder.getIntNTy(128);
+ auto i256 = Type::Word;
+ auto x_lo = m_builder.CreateZExt(m_builder.CreateTrunc(x, i128, "x.lo"), i256);
+ auto y_lo = m_builder.CreateZExt(m_builder.CreateTrunc(y, i128, "y.lo"), i256);
+ auto x_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256);
+ auto y_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256);
+
+ auto t1 = createCall(getMulFunc(), {x_lo, y_lo});
+ auto t2 = createCall(getMulFunc(), {x_lo, y_hi});
+ auto t3 = createCall(getMulFunc(), {x_hi, y_lo});
+ auto t4 = createCall(getMulFunc(), {x_hi, y_hi});
+
+ auto p = m_builder.CreateZExt(t1, i512);
+ p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i512), m_builder.getIntN(512, 128)));
+ p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i512), m_builder.getIntN(512, 128)));
+ p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i512), m_builder.getIntN(512, 256)));
+ m_builder.CreateRet(p);
}
- createCall(m_debug, {_value, m_builder.getInt8(_c)});
+ return func;
}
llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
@@ -85,6 +161,15 @@ llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0");
auto shlBy0 = m_builder.CreateICmpEQ(i0, zero);
auto y0 = m_builder.CreateShl(yArg, i0);
+ if (_type == m_builder.getIntNTy(512)) // Workaround for shl bug for long shifts
+ {
+ const auto treshold = m_builder.getIntN(512, 128);
+ auto highShift = m_builder.CreateICmpUGT(i0, treshold);
+ auto s = m_builder.CreateNUWSub(i0, treshold);
+ auto yhs = m_builder.CreateShl(yArg, treshold);
+ yhs = m_builder.CreateShl(yhs, s);
+ y0 = m_builder.CreateSelect(highShift, yhs, y0);
+ }
y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result
m_builder.CreateBr(loopBB);
@@ -135,7 +220,7 @@ llvm::Function* Arith256::getExpFunc()
if (!m_exp)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word};
- m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "arith.exp", getModule());
+ m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "exp", getModule());
auto base = &m_exp->getArgumentList().front();
base->setName("base");
@@ -159,9 +244,6 @@ llvm::Function* Arith256::getExpFunc()
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp);
m_builder.SetInsertPoint(entryBB);
- auto a1 = m_builder.CreateAlloca(Type::Word, nullptr, "a1");
- auto a2 = m_builder.CreateAlloca(Type::Word, nullptr, "a2");
- auto a3 = m_builder.CreateAlloca(Type::Word, nullptr, "a3");
m_builder.CreateBr(headerBB);
m_builder.SetInsertPoint(headerBB);
@@ -176,20 +258,14 @@ llvm::Function* Arith256::getExpFunc()
m_builder.CreateCondBr(eOdd, updateBB, continueBB);
m_builder.SetInsertPoint(updateBB);
- m_builder.CreateStore(r, a1);
- m_builder.CreateStore(b, a2);
- createCall(m_mul, {a1, a2, a3});
- auto r0 = m_builder.CreateLoad(a3, "r0");
+ auto r0 = createCall(getMulFunc(), {r, b});
m_builder.CreateBr(continueBB);
m_builder.SetInsertPoint(continueBB);
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1");
r1->addIncoming(r, bodyBB);
r1->addIncoming(r0, updateBB);
- m_builder.CreateStore(b, a1);
- m_builder.CreateStore(b, a2);
- createCall(m_mul, {a1, a2, a3});
- auto b1 = m_builder.CreateLoad(a3, "b1");
+ auto b1 = createCall(getMulFunc(), {b, b});
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1");
m_builder.CreateBr(headerBB);
@@ -244,9 +320,6 @@ llvm::Function* Arith256::getMulModFunc()
m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule());
auto i512Ty = m_builder.getIntNTy(512);
- llvm::Type* mul512ArgTypes[] = {Type::WordPtr, Type::WordPtr, i512Ty->getPointerTo()};
- auto mul512 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, mul512ArgTypes, false), llvm::Function::ExternalLinkage, "arith_mul512", getModule());
-
auto x = &m_mulmod->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
@@ -258,32 +331,19 @@ llvm::Function* Arith256::getMulModFunc()
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod);
m_builder.SetInsertPoint(entryBB);
- auto a1 = m_builder.CreateAlloca(Type::Word);
- auto a2 = m_builder.CreateAlloca(Type::Word);
- auto a3 = m_builder.CreateAlloca(i512Ty);
- m_builder.CreateStore(x, a1);
- m_builder.CreateStore(y, a2);
- createCall(mul512, {a1, a2, a3});
- auto p = m_builder.CreateLoad(a3, "p");
+ auto p = createCall(getMul512Func(), {x, y});
auto m = m_builder.CreateZExt(mod, i512Ty, "m");
auto d = createCall(getDivFunc(i512Ty), {p, m});
auto r = m_builder.CreateExtractValue(d, 1, "r");
- m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word));
+ r = m_builder.CreateTrunc(r, Type::Word);
+ m_builder.CreateRet(r);
}
return m_mulmod;
}
-llvm::Value* Arith256::binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2)
-{
- m_builder.CreateStore(_arg1, m_arg1);
- m_builder.CreateStore(_arg2, m_arg2);
- m_builder.CreateCall3(_op, m_arg1, m_arg2, m_result);
- return m_builder.CreateLoad(m_result);
-}
-
llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2)
{
- return binaryOp(m_mul, _arg1, _arg2);
+ return createCall(getMulFunc(), {_arg1, _arg2});
}
std::pair Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2)
@@ -331,157 +391,6 @@ llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Valu
return createCall(getMulModFunc(), {_arg1, _arg2, _arg3});
}
-namespace
-{
-#ifdef __SIZEOF_INT128__
- using uint128 = __uint128_t;
-#else
- struct uint128
- {
- uint64_t lo = 0;
- uint64_t hi = 0;
-
- uint128(uint64_t lo) : lo(lo) {}
-
- uint128 operator+(uint128 a)
- {
- uint128 r = 0;
- bool overflow = lo > std::numeric_limits::max() - a.lo;
- r.lo = lo + a.lo;
- r.hi = hi + a.hi + overflow;
- return r;
- }
-
- uint128 operator>>(int s)
- {
- assert(s == 64);
- return hi;
- }
-
- uint128 operator<<(int s)
- {
- assert(s == 64);
- uint128 r = 0;
- r.hi = lo;
- return r;
- }
-
- explicit operator uint64_t() { return lo; }
-
- static uint128 mul(uint64_t a, uint64_t b)
- {
- auto x_lo = 0xFFFFFFFF & a;
- auto y_lo = 0xFFFFFFFF & b;
- auto x_hi = a >> 32;
- auto y_hi = b >> 32;
-
- auto t1 = x_lo * y_lo;
- auto t2 = x_lo * y_hi;
- auto t3 = x_hi * y_lo;
- auto t4 = x_hi * y_hi;
-
- auto lo = (uint32_t)t1;
- auto mid = (uint64_t)(t1 >> 32) + (uint32_t)t2 + (uint32_t)t3;
- auto hi = (uint64_t)(t2 >> 32) + (t3 >> 32) + t4 + (mid >> 32);
-
- uint128 r = 0;
- r.lo = (uint64_t)lo + (mid << 32);
- r.hi = hi;
- return r;
- }
-
- uint128 operator*(uint128 a)
- {
- auto t1 = mul(lo, a.lo);
- auto t2 = mul(lo, a.hi);
- auto t3 = mul(hi, a.lo);
- return t1 + (t2 << 64) + (t3 << 64);
- }
- };
-#endif
-
- struct uint256
- {
- uint64_t lo = 0;
- uint64_t mid = 0;
- uint128 hi = 0;
-
- uint256(uint64_t lo, uint64_t mid, uint128 hi): lo(lo), mid(mid), hi(hi) {}
- uint256(uint128 n)
- {
- lo = (uint64_t) n;
- mid = (uint64_t) (n >> 64);
- }
-
- explicit operator uint128()
- {
- uint128 r = lo;
- r = (r + ((uint128) mid)) << 64;
- return r;
- }
-
- uint256 operator+(uint256 a)
- {
- auto _lo = (uint128) lo + a.lo;
- auto _mid = (uint128) mid + a.mid + (_lo >> 64);
- auto _hi = hi + a.hi + (_mid >> 64);
- return {(uint64_t)_lo, (uint64_t)_mid, _hi};
- }
-
- uint256 lo2hi()
- {
- hi = (uint128)*this;
- lo = 0;
- mid = 0;
- return *this;
- }
- };
-
- struct uint512
- {
- uint128 lo;
- uint128 mid;
- uint256 hi;
- };
-
- uint256 mul(uint256 x, uint256 y)
- {
- auto t1 = (uint128) x.lo * y.lo;
- auto t2 = (uint128) x.lo * y.mid;
- auto t3 = (uint128) x.lo * y.hi;
- auto t4 = (uint128) x.mid * y.lo;
- auto t5 = (uint128) x.mid * y.mid;
- auto t6 = (uint128) x.mid * y.hi;
- auto t7 = x.hi * y.lo;
- auto t8 = x.hi * y.mid;
-
- auto lo = (uint64_t) t1;
- auto m1 = (t1 >> 64) + (uint64_t) t2;
- auto m2 = (uint64_t) m1;
- auto mid = (uint128) m2 + (uint64_t) t4;
- auto hi = (t2 >> 64) + t3 + (t4 >> 64) + t5 + (t6 << 64) + t7
- + (t8 << 64) + (m1 >> 64) + (mid >> 64);
-
- return {lo, (uint64_t)mid, hi};
- }
-
- uint512 mul512(uint256 x, uint256 y)
- {
- auto x_lo = (uint128) x;
- auto y_lo = (uint128) y;
-
- auto t1 = mul(x_lo, y_lo);
- auto t2 = mul(x_lo, y.hi);
- auto t3 = mul(x.hi, y_lo);
- auto t4 = mul(x.hi, y.hi);
-
- auto lo = (uint128) t1;
- auto mid = (uint256) t1.hi + (uint128) t2 + (uint128) t3;
- auto hi = (uint256)t2.hi + t3.hi + t4 + mid.hi;
-
- return {lo, (uint128)mid, hi};
- }
-}
}
}
@@ -489,20 +398,9 @@ namespace
extern "C"
{
- using namespace dev::eth::jit;
-
EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z)
{
- std::cerr << "DEBUG " << z << ": " << d << c << b << a << std::endl;
- }
-
- EXPORT void arith_mul(uint256* _arg1, uint256* _arg2, uint256* o_result)
- {
- *o_result = mul(*_arg1, *_arg2);
- }
-
- EXPORT void arith_mul512(uint256* _arg1, uint256* _arg2, uint512* o_result)
- {
- *o_result = mul512(*_arg1, *_arg2);
+ std::cerr << "DEBUG " << std::dec << z << ": " //<< d << c << b << a
+ << " [" << std::hex << std::setfill('0') << std::setw(16) << d << std::setw(16) << c << std::setw(16) << b << std::setw(16) << a << "]\n";
}
}
diff --git a/evmjit/libevmjit/Arith256.h b/evmjit/libevmjit/Arith256.h
index 5852137f8..2513ca568 100644
--- a/evmjit/libevmjit/Arith256.h
+++ b/evmjit/libevmjit/Arith256.h
@@ -24,26 +24,21 @@ public:
void debug(llvm::Value* _value, char _c);
private:
+ llvm::Function* getMulFunc();
+ llvm::Function* getMul512Func();
llvm::Function* getDivFunc(llvm::Type* _type);
llvm::Function* getExpFunc();
llvm::Function* getAddModFunc();
llvm::Function* getMulModFunc();
- llvm::Value* binaryOp(llvm::Function* _op, llvm::Value* _arg1, llvm::Value* _arg2);
-
- llvm::Function* m_mul;
-
+ llvm::Function* m_mul = nullptr;
+ llvm::Function* m_mul512 = nullptr;
llvm::Function* m_div = nullptr;
llvm::Function* m_div512 = nullptr;
llvm::Function* m_exp = nullptr;
llvm::Function* m_addmod = nullptr;
llvm::Function* m_mulmod = nullptr;
llvm::Function* m_debug = nullptr;
-
- llvm::Value* m_arg1;
- llvm::Value* m_arg2;
- llvm::Value* m_arg3;
- llvm::Value* m_result;
};
diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp
index 5868c0f43..c9e71be9a 100644
--- a/evmjit/libevmjit/BasicBlock.cpp
+++ b/evmjit/libevmjit/BasicBlock.cpp
@@ -1,13 +1,14 @@
-
#include "BasicBlock.h"
#include
+#include "preprocessor/llvm_includes_start.h"
#include
#include
#include
#include
#include
+#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
@@ -18,13 +19,14 @@ namespace eth
namespace jit
{
-const char* BasicBlock::NamePrefix = "Instr.";
+static const char* jumpDestName = "JmpDst.";
+static const char* basicBlockName = "Instr.";
-BasicBlock::BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) :
+BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) :
+ m_firstInstrIdx{_firstInstrIdx},
m_begin(_begin),
m_end(_end),
- // TODO: Add begin index to name
- m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), NamePrefix, _mainFunc)),
+ m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)),
m_stack(*this),
m_builder(_builder),
m_isJumpDest(isJumpDest)
@@ -43,6 +45,7 @@ BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) :
void BasicBlock::LocalStack::push(llvm::Value* _value)
{
+ assert(_value->getType() == Type::Word);
m_bblock.m_currentStack.push_back(_value);
m_bblock.m_tosOffset += 1;
}
@@ -139,7 +142,7 @@ void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
auto endIter = m_currentStack.end();
// Update (emit set()) changed values
- for (int idx = m_currentStack.size() - 1 - m_tosOffset;
+ for (int idx = (int)m_currentStack.size() - 1 - m_tosOffset;
currIter < endIter && idx >= 0;
++currIter, --idx)
{
@@ -306,7 +309,7 @@ void BasicBlock::linkLocalStacks(std::vector basicBlocks, llvm::IRB
auto& initialStack = bblock.m_initialStack;
initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems);
// Initial stack shrinks, so the size difference grows:
- bblock.m_tosOffset += info.inputItems;
+ bblock.m_tosOffset += (int)info.inputItems;
}
// We must account for the items that were pushed directly to successor
@@ -319,7 +322,7 @@ void BasicBlock::linkLocalStacks(std::vector basicBlocks, llvm::IRB
auto& exitStack = bblock.m_currentStack;
exitStack.erase(exitStack.end() - info.outputItems, exitStack.end());
- bblock.m_tosOffset -= info.outputItems;
+ bblock.m_tosOffset -= (int)info.outputItems; // FIXME: Fix types
}
}
diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h
index cda4f89bd..7469b7b69 100644
--- a/evmjit/libevmjit/BasicBlock.h
+++ b/evmjit/libevmjit/BasicBlock.h
@@ -1,6 +1,7 @@
#pragma once
+
#include
-#include
+
#include "Common.h"
#include "Stack.h"
@@ -11,7 +12,7 @@ namespace eth
namespace jit
{
-using ProgramCounter = uint64_t; // TODO: Rename
+using instr_idx = uint64_t;
class BasicBlock
{
@@ -50,10 +51,7 @@ public:
BasicBlock& m_bblock;
};
- /// Basic block name prefix. The rest is instruction index.
- static const char* NamePrefix;
-
- explicit BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
+ explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
BasicBlock(const BasicBlock&) = delete;
@@ -61,8 +59,9 @@ public:
llvm::BasicBlock* llvm() { return m_llvmBB; }
- bytes::const_iterator begin() { return m_begin; }
- bytes::const_iterator end() { return m_end; }
+ instr_idx firstInstrIdx() const { return m_firstInstrIdx; }
+ code_iterator begin() const { return m_begin; }
+ code_iterator end() const { return m_end; }
bool isJumpDest() const { return m_isJumpDest; }
@@ -84,8 +83,9 @@ public:
void dump(std::ostream& os, bool _dotOutput = false);
private:
- bytes::const_iterator const m_begin;
- bytes::const_iterator const m_end;
+ instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block
+ code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block
+ code_iterator const m_end = {}; ///< Iterator pointing code end of the block
llvm::BasicBlock* const m_llvmBB;
diff --git a/evmjit/libevmjit/BuildInfo.h.in b/evmjit/libevmjit/BuildInfo.h.in
new file mode 100644
index 000000000..204b4d89b
--- /dev/null
+++ b/evmjit/libevmjit/BuildInfo.h.in
@@ -0,0 +1,10 @@
+
+#define EVMJIT_VERSION "${EVMJIT_VERSION}"
+#define EVMJIT_VERSION_MAJOR ${EVMJIT_VERSION_MAJOR}
+#define EVMJIT_VERSION_MINOR ${EVMJIT_VERSION_MINOR}
+#define EVMJIT_VERSION_PATCH ${EVMJIT_VERSION_PATCH}
+#define EVMJIT_VERSION_PRERELEASE "${EVMJIT_VERSION_PRERELEASE}"
+#define EVMJIT_VERSION_FULL "${EVMJIT_VERSION_FULL}"
+
+#define LLVM_VERSION "${LLVM_PACKAGE_VERSION}"
+#define LLVM_ASSERTIONS "${LLVM_ENABLE_ASSERTIONS}"
diff --git a/evmjit/libevmjit/CMakeLists.txt b/evmjit/libevmjit/CMakeLists.txt
index 545e55344..943c64e42 100644
--- a/evmjit/libevmjit/CMakeLists.txt
+++ b/evmjit/libevmjit/CMakeLists.txt
@@ -6,30 +6,63 @@ set(INTERFACE_HEADERS interface.h)
source_group("" FILES ${HEADERS})
source_group("" FILES ${SOURCES})
-if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
- # Disable rtti for Cache as LLVM has no rtti
- set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti)
+if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
+else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif()
-find_package(Git)
-if(GIT_FOUND)
- execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always
- OUTPUT_VARIABLE EVMJIT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+set(EVMJIT_VERSION "0.0.0")
+set(EVMJIT_VERSION_MAJOR 0)
+set(EVMJIT_VERSION_MINOR 0)
+set(EVMJIT_VERSION_PATCH 0)
+set(EVMJIT_VERSION_FULL "v0.0.0-nogit")
+
+find_package(Git)
+if(GIT_FOUND)
+ execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always --match v*
+ OUTPUT_VARIABLE EVMJIT_VERSION_FULL OUTPUT_STRIP_TRAILING_WHITESPACE)
+endif()
+
+if(${EVMJIT_VERSION_FULL} MATCHES "^v[0-9]+\\.[0-9]+")
+ string(SUBSTRING ${EVMJIT_VERSION_FULL} 1 -1 EVMJIT_VERSION_FULL) # skip "v"
+ string(REPLACE "-" ";" VERSION_COMPONENTS ${EVMJIT_VERSION_FULL})
+ list(LENGTH VERSION_COMPONENTS NUM_VERSION_COMPONENTS)
+ list(GET VERSION_COMPONENTS 0 EVMJIT_VERSION)
+ string(REPLACE "." ";" VERSION_NUMBERS ${EVMJIT_VERSION})
+ list(LENGTH VERSION_NUMBERS NUM_VERSION_NUMBERS)
+ list(GET VERSION_NUMBERS 0 EVMJIT_VERSION_MAJOR)
+ list(GET VERSION_NUMBERS 1 EVMJIT_VERSION_MINOR)
+ if(${NUM_VERSION_NUMBERS} GREATER 2)
+ list(GET VERSION_NUMBERS 2 EVMJIT_VERSION_PATCH) # patch number is optional
+ endif()
+ if(${NUM_VERSION_COMPONENTS} GREATER 1)
+ list(GET VERSION_COMPONENTS 1 VERSION_PRERELEASE_CANDIDATE)
+ string(REGEX MATCH "^[a-zA-Z]+.*" EVMJIT_VERSION_PRERELEASE ${VERSION_PRERELEASE_CANDIDATE}) # prerelease starts with letter
+ endif()
endif()
-if(NOT EVMJIT_VERSION)
- set(EVMJIT_VERSION "unknown")
+
+if(${EVMJIT_VERSION_MAJOR} EQUAL 0)
+ set(EVMJIT_SOVERSION "0.${EVMJIT_VERSION_MINOR}")
+else()
+ set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR})
endif()
-message("EVM JIT version: ${EVMJIT_VERSION}")
+configure_file(BuildInfo.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen/BuildInfo.gen.h)
+
+message(STATUS "EVM JIT version: ${EVMJIT_VERSION_MAJOR}.${EVMJIT_VERSION_MINOR}.${EVMJIT_VERSION_PATCH} ${EVMJIT_VERSION_PRERELEASE} (${EVMJIT_VERSION_FULL})")
-add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS})
-set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${EVMJIT_VERSION} FOLDER "libs")
+add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS} gen/BuildInfo.gen.h)
+set_target_properties(${TARGET_NAME} PROPERTIES
+ VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION}
+ FOLDER "libs")
include_directories(${LLVM_INCLUDE_DIRS})
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen)
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS})
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
-#install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME})
+#install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME})
\ No newline at end of file
diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp
index 0e6fb517a..fe226eefb 100644
--- a/evmjit/libevmjit/Cache.cpp
+++ b/evmjit/libevmjit/Cache.cpp
@@ -1,12 +1,19 @@
#include "Cache.h"
-#include
-#include
+
#include
+#include
+
+#include "preprocessor/llvm_includes_start.h"
#include
#include
+#include
#include
#include
#include
+#include "preprocessor/llvm_includes_end.h"
+
+#include "ExecutionEngine.h"
+#include "Utils.h"
namespace dev
{
@@ -15,23 +22,31 @@ namespace eth
namespace jit
{
-//#define LOG(...) std::cerr << "CACHE "
-#define LOG(...) std::ostream(nullptr)
+//#define CACHE_LOG std::cerr << "CACHE "
+#define CACHE_LOG std::ostream(nullptr)
-ObjectCache* Cache::getObjectCache()
+namespace
{
- static ObjectCache objectCache;
- return &objectCache;
+ llvm::MemoryBuffer* g_lastObject;
+ ExecutionEngineListener* g_listener;
}
-namespace
+ObjectCache* Cache::getObjectCache(ExecutionEngineListener* _listener)
{
- llvm::MemoryBuffer* lastObject;
+ static ObjectCache objectCache;
+ g_listener = _listener;
+ return &objectCache;
}
std::unique_ptr Cache::getObject(std::string const& id)
{
- assert(!lastObject);
+ if (g_listener)
+ g_listener->stateChanged(ExecState::CacheLoad);
+
+ CACHE_LOG << id << ": search\n";
+ if (!CHECK(!g_lastObject))
+ g_lastObject = nullptr;
+
llvm::SmallString<256> cachePath;
llvm::sys::path::system_temp_directory(false, cachePath);
llvm::sys::path::append(cachePath, "evm_objs", id);
@@ -51,22 +66,31 @@ std::unique_ptr Cache::getObject(std::string const& id)
#endif
if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false))
- lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer());
+ g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer());
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory))
std::cerr << r.getError().message(); // TODO: Add log
- if (lastObject) // if object found create fake module
+ if (g_lastObject) // if object found create fake module
{
- auto module = std::unique_ptr(new llvm::Module(id, llvm::getGlobalContext()));
- auto mainFuncType = llvm::FunctionType::get(llvm::IntegerType::get(llvm::getGlobalContext(), 32), {}, false);
- llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get());
+ CACHE_LOG << id << ": found\n";
+ auto&& context = llvm::getGlobalContext();
+ auto module = std::unique_ptr(new llvm::Module(id, context));
+ auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(context), {}, false);
+ auto mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get());
+ auto bb = llvm::BasicBlock::Create(context, {}, mainFunc);
+ bb->getInstList().push_back(new llvm::UnreachableInst{context});
+ return module;
}
+ CACHE_LOG << id << ": not found\n";
return nullptr;
}
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object)
{
+ if (g_listener)
+ g_listener->stateChanged(ExecState::CacheWrite);
+
auto&& id = _module->getModuleIdentifier();
llvm::SmallString<256> cachePath;
llvm::sys::path::system_temp_directory(false, cachePath);
@@ -77,15 +101,17 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory
llvm::sys::path::append(cachePath, id);
+ CACHE_LOG << id << ": write\n";
std::string error;
llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None);
cacheFile << _object->getBuffer();
}
-llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const*)
+llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module)
{
- auto o = lastObject;
- lastObject = nullptr;
+ CACHE_LOG << _module->getModuleIdentifier() << ": use\n";
+ auto o = g_lastObject;
+ g_lastObject = nullptr;
return o;
}
diff --git a/evmjit/libevmjit/Cache.h b/evmjit/libevmjit/Cache.h
index 1cad537cd..e8f01d38d 100644
--- a/evmjit/libevmjit/Cache.h
+++ b/evmjit/libevmjit/Cache.h
@@ -1,9 +1,8 @@
#pragma once
#include
-#include
-#include
+#include
namespace dev
{
@@ -11,6 +10,7 @@ namespace eth
{
namespace jit
{
+class ExecutionEngineListener;
class ObjectCache : public llvm::ObjectCache
{
@@ -23,16 +23,13 @@ public:
/// not available. The caller owns both the MemoryBuffer returned by this
/// and the memory it references.
virtual llvm::MemoryBuffer* getObject(llvm::Module const* _module) final override;
-
-private:
- std::unordered_map> m_map;
};
class Cache
{
public:
- static ObjectCache* getObjectCache();
+ static ObjectCache* getObjectCache(ExecutionEngineListener* _listener);
static std::unique_ptr getObject(std::string const& id);
};
diff --git a/evmjit/libevmjit/Common.h b/evmjit/libevmjit/Common.h
index d629f25ad..62731292f 100644
--- a/evmjit/libevmjit/Common.h
+++ b/evmjit/libevmjit/Common.h
@@ -20,25 +20,30 @@ namespace jit
using byte = uint8_t;
using bytes = std::vector;
using bytes_ref = std::tuple;
+using code_iterator = byte const*;
struct NoteChannel {}; // FIXME: Use some log library?
enum class ReturnCode
{
- Stop = 0,
- Return = 1,
+ // Success codes
+ Stop = 0,
+ Return = 1,
Suicide = 2,
- OutOfGas = -1,
- BadJumpDestination = -2,
- StackTooSmall = -3,
- BadInstruction = -4,
+ // Standard error codes
+ OutOfGas = -1,
+ StackTooSmall = -2,
+ BadJumpDestination = -3,
+ BadInstruction = -4,
+ Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
- LLVMConfigError = -5,
- LLVMCompileError = -6,
- LLVMLinkError = -7,
+ // Internal error codes
+ LLVMConfigError = -101,
+ LLVMCompileError = -102,
+ LLVMLinkError = -103,
- UnexpectedException = -8,
+ UnexpectedException = -111,
LinkerWorkaround = -299,
};
diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp
index ad8a14fc7..de48e8ef9 100644
--- a/evmjit/libevmjit/Compiler.cpp
+++ b/evmjit/libevmjit/Compiler.cpp
@@ -1,4 +1,3 @@
-
#include "Compiler.h"
#include
@@ -6,13 +5,15 @@
#include
#include
+#include "preprocessor/llvm_includes_start.h"
#include
#include
#include
#include
-
+#include
#include
#include
+#include "preprocessor/llvm_includes_end.h"
#include "Instruction.h"
#include "Type.h"
@@ -39,10 +40,10 @@ Compiler::Compiler(Options const& _options):
Type::init(m_builder.getContext());
}
-void Compiler::createBasicBlocks(bytes const& _bytecode)
+void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd)
{
/// Helper function that skips push data and finds next iterator (can be the end)
- auto skipPushDataAndGetNext = [](bytes::const_iterator _curr, bytes::const_iterator _end)
+ auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end)
{
static const auto push1 = static_cast(Instruction::PUSH1);
static const auto push32 = static_cast(Instruction::PUSH32);
@@ -52,11 +53,11 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
return _curr + offset;
};
- auto begin = _bytecode.begin();
+ auto begin = _codeBegin; // begin of current block
bool nextJumpDest = false;
- for (auto curr = begin, next = begin; curr != _bytecode.end(); curr = next)
+ for (auto curr = begin, next = begin; curr != _codeEnd; curr = next)
{
- next = skipPushDataAndGetNext(curr, _bytecode.end());
+ next = skipPushDataAndGetNext(curr, _codeEnd);
bool isEnd = false;
switch (Instruction(*curr))
@@ -77,22 +78,19 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
break;
}
- assert(next <= _bytecode.end());
- if (next == _bytecode.end() || Instruction(*next) == Instruction::JUMPDEST)
+ assert(next <= _codeEnd);
+ if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST)
isEnd = true;
if (isEnd)
{
- auto beginIdx = begin - _bytecode.begin();
+ auto beginIdx = begin - _codeBegin;
m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx),
- std::forward_as_tuple(begin, next, m_mainFunc, m_builder, nextJumpDest));
+ std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, m_builder, nextJumpDest));
nextJumpDest = false;
begin = next;
}
}
-
- // TODO: Create Stop basic block on demand
- m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
}
llvm::BasicBlock* Compiler::getJumpTableBlock()
@@ -125,7 +123,7 @@ llvm::BasicBlock* Compiler::getBadJumpBlock()
return m_badJumpBlock->llvm();
}
-std::unique_ptr Compiler::compile(bytes const& _bytecode, std::string const& _id)
+std::unique_ptr Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id)
{
auto compilationStartTime = std::chrono::high_resolution_clock::now();
auto module = std::unique_ptr(new llvm::Module(_id, m_builder.getContext()));
@@ -135,21 +133,40 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str
m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get());
m_mainFunc->getArgumentList().front().setName("rt");
- // Create the basic blocks.
- auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc);
+ // Create entry basic block
+ auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc);
m_builder.SetInsertPoint(entryBlock);
- createBasicBlocks(_bytecode);
+ auto jmpBufWords = m_builder.CreateAlloca(Type::BytePtr, m_builder.getInt64(3), "jmpBuf.words");
+ auto frameaddress = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::frameaddress);
+ auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp");
+ m_builder.CreateStore(fp, jmpBufWords);
+ auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave);
+ auto sp = m_builder.CreateCall(stacksave, "sp");
+ auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp");
+ m_builder.CreateStore(sp, jmpBufSp);
+ auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp);
+ auto jmpBuf = m_builder.CreateBitCast(jmpBufWords, Type::BytePtr, "jmpBuf");
+ auto r = m_builder.CreateCall(setjmp, jmpBuf);
+ auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0));
+
+ createBasicBlocks(_begin, _end);
// Init runtime structures.
- RuntimeManager runtimeManager(m_builder);
+ RuntimeManager runtimeManager(m_builder, jmpBuf, _begin, _end);
GasMeter gasMeter(m_builder, runtimeManager);
Memory memory(runtimeManager, gasMeter);
Ext ext(runtimeManager, memory);
Stack stack(m_builder, runtimeManager);
Arith256 arith(m_builder);
- m_builder.CreateBr(m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm());
+ // TODO: Create Stop basic block on demand
+ m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
+ auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
+
+ auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm();
+ auto expectTrue = llvm::MDBuilder{m_builder.getContext()}.createBranchWeights(1, 0);
+ m_builder.CreateCondBr(normalFlow, firstBB, abortBB, expectTrue);
for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt)
{
@@ -157,7 +174,7 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str
auto iterCopy = basicBlockPairIt;
++iterCopy;
auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr;
- compileBasicBlock(basicBlock, _bytecode, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock);
+ compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock);
}
// Code for special blocks:
@@ -165,6 +182,9 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str
m_builder.SetInsertPoint(m_stopBB);
m_builder.CreateRet(Constant::get(ReturnCode::Stop));
+ m_builder.SetInsertPoint(abortBB);
+ m_builder.CreateRet(Constant::get(ReturnCode::OutOfGas));
+
removeDeadBlocks();
// Link jump table target index
@@ -224,7 +244,7 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str
}
-void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, RuntimeManager& _runtimeManager,
+void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager,
Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock)
{
if (!_nextBasicBlock) // this is the last block in the code
@@ -623,7 +643,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::PC:
{
- auto value = Constant::get(it - _bytecode.begin());
+ auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx());
stack.push(value);
break;
}
@@ -631,7 +651,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::GAS:
{
_gasMeter.commitCostBlock();
- stack.push(_runtimeManager.getGas());
+ stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word));
break;
}
@@ -741,10 +761,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
_memory.require(initOff, initSize);
_gasMeter.commitCostBlock();
-
- auto gas = _runtimeManager.getGas();
- auto address = _ext.create(gas, endowment, initOff, initSize);
- _runtimeManager.setGas(gas);
+ auto address = _ext.create(endowment, initOff, initSize);
stack.push(address);
break;
}
@@ -752,7 +769,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::CALL:
case Instruction::CALLCODE:
{
- auto gas = stack.pop();
+ auto callGas256 = stack.pop();
auto codeAddress = stack.pop();
auto value = stack.pop();
auto inOff = stack.pop();
@@ -770,9 +787,13 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
if (inst == Instruction::CALLCODE)
receiveAddress = _runtimeManager.get(RuntimeData::Address);
- _gasMeter.count(gas);
- auto ret = _ext.call(gas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress);
- _gasMeter.giveBack(gas);
+ auto gas = _runtimeManager.getGas();
+ _gasMeter.count(callGas256);
+ auto callGas = m_builder.CreateTrunc(callGas256, Type::Gas);
+ auto gasLeft = m_builder.CreateNSWSub(gas, callGas);
+ _runtimeManager.setGas(callGas);
+ auto ret = _ext.call(receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress);
+ _gasMeter.giveBack(gasLeft);
stack.push(ret);
break;
}
@@ -825,12 +846,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
break;
}
- default: // Invalid instruction - runtime exception
- {
- // TODO: Replace with return statement
- _runtimeManager.raiseException(ReturnCode::BadInstruction);
- }
-
+ default: // Invalid instruction - abort
+ m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction));
+ it = _basicBlock.end() - 1; // finish block compilation
}
}
diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h
index 720a48cf9..c9795fb99 100644
--- a/evmjit/libevmjit/Compiler.h
+++ b/evmjit/libevmjit/Compiler.h
@@ -1,8 +1,5 @@
-
#pragma once
-#include
-
#include "Common.h"
#include "BasicBlock.h"
@@ -33,13 +30,13 @@ public:
Compiler(Options const& _options);
- std::unique_ptr compile(bytes const& _bytecode, std::string const& _id);
+ std::unique_ptr compile(code_iterator _begin, code_iterator _end, std::string const& _id);
private:
- void createBasicBlocks(bytes const& _bytecode);
+ void createBasicBlocks(code_iterator _begin, code_iterator _end);
- void compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock);
+ void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock);
llvm::BasicBlock* getJumpTableBlock();
diff --git a/evmjit/libevmjit/CompilerHelper.cpp b/evmjit/libevmjit/CompilerHelper.cpp
index 9cccecd79..5c8ee8574 100644
--- a/evmjit/libevmjit/CompilerHelper.cpp
+++ b/evmjit/libevmjit/CompilerHelper.cpp
@@ -1,8 +1,8 @@
-
#include "CompilerHelper.h"
-#include
+#include "preprocessor/llvm_includes_start.h"
#include
+#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
diff --git a/evmjit/libevmjit/CompilerHelper.h b/evmjit/libevmjit/CompilerHelper.h
index 62733ca72..cd6d09a58 100644
--- a/evmjit/libevmjit/CompilerHelper.h
+++ b/evmjit/libevmjit/CompilerHelper.h
@@ -1,7 +1,8 @@
-
#pragma once
+#include "preprocessor/llvm_includes_start.h"
#include
+#include "preprocessor/llvm_includes_end.h"
namespace dev
@@ -19,7 +20,7 @@ protected:
CompilerHelper(llvm::IRBuilder<>& _builder);
CompilerHelper(const CompilerHelper&) = delete;
- void operator=(CompilerHelper) = delete;
+ CompilerHelper& operator=(CompilerHelper) = delete;
/// Reference to the IR module being compiled
llvm::Module* getModule();
diff --git a/evmjit/libevmjit/Endianness.cpp b/evmjit/libevmjit/Endianness.cpp
index db7edfdc9..38f71560c 100644
--- a/evmjit/libevmjit/Endianness.cpp
+++ b/evmjit/libevmjit/Endianness.cpp
@@ -1,7 +1,8 @@
-
#include "Endianness.h"
+#include "preprocessor/llvm_includes_start.h"
#include
+#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
diff --git a/evmjit/libevmjit/Endianness.h b/evmjit/libevmjit/Endianness.h
index 8a1f41085..19fd8fc58 100644
--- a/evmjit/libevmjit/Endianness.h
+++ b/evmjit/libevmjit/Endianness.h
@@ -1,7 +1,8 @@
-
#pragma once
+#include "preprocessor/llvm_includes_start.h"
#include
+#include "preprocessor/llvm_includes_end.h"
namespace dev
{
diff --git a/evmjit/libevmjit/ExecStats.cpp b/evmjit/libevmjit/ExecStats.cpp
new file mode 100644
index 000000000..684f6d39a
--- /dev/null
+++ b/evmjit/libevmjit/ExecStats.cpp
@@ -0,0 +1,97 @@
+#include "ExecStats.h"
+
+#include
+#include
+#include
+
+#include "Utils.h"
+
+namespace dev
+{
+namespace eth
+{
+namespace jit
+{
+
+void ExecStats::stateChanged(ExecState _state)
+{
+ if (!CHECK(m_state != ExecState::Finished))
+ return;
+
+ auto now = clock::now();
+ if (_state != ExecState::Started)
+ {
+ assert(time[(int)m_state] == ExecStats::duration::zero());
+ time[(int)m_state] = now - m_tp;
+ }
+ m_state = _state;
+ m_tp = now;
+}
+
+namespace
+{
+struct StatsAgg
+{
+ using unit = std::chrono::microseconds;
+ ExecStats::duration tot = ExecStats::duration::zero();
+ ExecStats::duration min = ExecStats::duration::max();
+ ExecStats::duration max = ExecStats::duration::zero();
+ size_t count = 0;
+
+ void update(ExecStats::duration _d)
+ {
+ ++count;
+ tot += _d;
+ min = _d < min ? _d : min;
+ max = _d > max ? _d : max;
+ }
+
+ void output(char const* _name, std::ostream& _os)
+ {
+ auto avg = tot / count;
+ _os << std::setfill(' ')
+ << std::setw(12) << std::left << _name
+ << std::setw(10) << std::right << std::chrono::duration_cast(tot).count()
+ << std::setw(10) << std::right << std::chrono::duration_cast(avg).count()
+ << std::setw(10) << std::right << std::chrono::duration_cast(min).count()
+ << std::setw(10) << std::right << std::chrono::duration_cast(max).count()
+ << std::endl;
+ }
+};
+
+char const* getExecStateName(ExecState _state)
+{
+ switch (_state)
+ {
+ case ExecState::Started: return "Start";
+ case ExecState::CacheLoad: return "CacheLoad";
+ case ExecState::CacheWrite: return "CacheWrite";
+ case ExecState::Compilation: return "Compilation";
+ case ExecState::CodeGen: return "CodeGen";
+ case ExecState::Execution: return "Execution";
+ case ExecState::Return: return "Return";
+ case ExecState::Finished: return "Finish";
+ }
+ return nullptr;
+}
+}
+
+StatsCollector::~StatsCollector()
+{
+ if (stats.empty())
+ return;
+
+ std::cout << " [us] total avg min max\n";
+ for (int i = 0; i < (int)ExecState::Finished; ++i)
+ {
+ StatsAgg agg;
+ for (auto&& s : stats)
+ agg.update(s->time[i]);
+
+ agg.output(getExecStateName(ExecState(i)), std::cout);
+ }
+}
+
+}
+}
+}
diff --git a/evmjit/libevmjit/ExecStats.h b/evmjit/libevmjit/ExecStats.h
new file mode 100644
index 000000000..498e21341
--- /dev/null
+++ b/evmjit/libevmjit/ExecStats.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include
+
+#include "ExecutionEngine.h"
+
+namespace dev
+{
+namespace eth
+{
+namespace jit
+{
+
+class ExecStats : public ExecutionEngineListener
+{
+public:
+ using clock = std::chrono::high_resolution_clock;
+ using duration = clock::duration;
+ using time_point = clock::time_point;
+
+ std::string id;
+ duration time[(int)ExecState::Finished] = {};
+
+ void stateChanged(ExecState _state) override;
+
+private:
+ ExecState m_state = {};
+ time_point m_tp = {};
+
+};
+
+
+class StatsCollector
+{
+public:
+ std::vector> stats;
+
+ ~StatsCollector();
+};
+
+}
+}
+}
diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp
index aae5349d8..1d2ff91b1 100644
--- a/evmjit/libevmjit/ExecutionEngine.cpp
+++ b/evmjit/libevmjit/ExecutionEngine.cpp
@@ -1,24 +1,25 @@
#include "ExecutionEngine.h"
-#include
+#include
#include // env options
+#include
+#include "preprocessor/llvm_includes_start.h"
#include
#include
-#pragma warning(push)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-parameter"
#include
#include
-#pragma warning(pop)
-#pragma GCC diagnostic pop
#include
#include
#include
+#include "preprocessor/llvm_includes_end.h"
#include "Runtime.h"
#include "Compiler.h"
#include "Cache.h"
+#include "ExecStats.h"
+#include "Utils.h"
+#include "BuildInfo.gen.h"
namespace dev
{
@@ -31,34 +32,20 @@ namespace
{
using EntryFuncPtr = ReturnCode(*)(Runtime*);
-ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime)
+std::string codeHash(i256 const& _hash)
{
- // That function uses long jumps to handle "execeptions".
- // Do not create any non-POD objects here
-
- ReturnCode returnCode{};
- auto sj = setjmp(_runtime->getJmpBuf());
- if (sj == 0)
- returnCode = _mainFunc(_runtime);
- else
- returnCode = static_cast(sj);
-
- return returnCode;
-}
-
-std::string codeHash(bytes const& _code)
-{
- uint32_t hash = 0;
- for (auto b : _code)
+ static const auto size = sizeof(_hash);
+ static const auto hexChars = "0123456789abcdef";
+ std::string str;
+ str.resize(size * 2);
+ auto outIt = str.rbegin(); // reverse for BE
+ auto& arr = *(std::array*)&_hash;
+ for (auto b : arr)
{
- hash += b;
- hash += (hash << 10);
- hash ^= (hash >> 6);
+ *(outIt++) = hexChars[b & 0xf];
+ *(outIt++) = hexChars[b >> 4];
}
- hash += (hash << 3);
- hash ^= (hash >> 11);
- hash += (hash << 15);
- return std::to_string(hash);
+ return str;
}
bool getEnvOption(char const* _name, bool _default)
@@ -69,82 +56,94 @@ bool getEnvOption(char const* _name, bool _default)
return std::strtol(var, nullptr, 10) != 0;
}
+bool showInfo()
+{
+ auto show = getEnvOption("EVMJIT_INFO", false);
+ if (show)
+ {
+ std::cout << "The Ethereum EVM JIT " EVMJIT_VERSION_FULL " LLVM " LLVM_VERSION << std::endl;
+ }
+ return show;
}
-ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env)
+}
+
+ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
{
- static std::unique_ptr ee; // TODO: Use Managed Objects from LLVM?
static auto debugDumpModule = getEnvOption("EVMJIT_DUMP", false);
static auto objectCacheEnabled = getEnvOption("EVMJIT_CACHE", true);
+ static auto statsCollectingEnabled = getEnvOption("EVMJIT_STATS", false);
+ static auto infoShown = showInfo();
+ (void) infoShown;
- auto mainFuncName = codeHash(_code);
- EntryFuncPtr entryFuncPtr{};
- Runtime runtime(_data, _env); // TODO: I don't know why but it must be created before getFunctionAddress() calls
+ std::unique_ptr listener{new ExecStats};
+ listener->stateChanged(ExecState::Started);
- if (ee && (entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName)))
+ auto objectCache = objectCacheEnabled ? Cache::getObjectCache(listener.get()) : nullptr;
+
+ static std::unique_ptr ee;
+ if (!ee)
{
+ llvm::InitializeNativeTarget();
+ llvm::InitializeNativeTargetAsmPrinter();
+
+ auto module = std::unique_ptr(new llvm::Module({}, llvm::getGlobalContext()));
+ llvm::EngineBuilder builder(module.get());
+ builder.setEngineKind(llvm::EngineKind::JIT);
+ builder.setUseMCJIT(true);
+ builder.setOptLevel(llvm::CodeGenOpt::None);
+
+ auto triple = llvm::Triple(llvm::sys::getProcessTriple());
+ if (triple.getOS() == llvm::Triple::OSType::Win32)
+ triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
+ module->setTargetTriple(triple.str());
+
+ ee.reset(builder.create());
+ if (!CHECK(ee))
+ return ReturnCode::LLVMConfigError;
+ module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module
+ ee->setObjectCache(objectCache);
}
- else
+
+ static StatsCollector statsCollector;
+
+ auto mainFuncName = codeHash(_data->codeHash);
+ Runtime runtime(_data, _env); // TODO: I don't know why but it must be created before getFunctionAddress() calls
+
+ auto entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
+ if (!entryFuncPtr)
{
- auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr;
- std::unique_ptr module;
- if (objectCache)
- module = Cache::getObject(mainFuncName);
+ auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr;
if (!module)
- module = Compiler({}).compile(_code, mainFuncName);
- if (debugDumpModule)
- module->dump();
- if (!ee)
- {
- llvm::InitializeNativeTarget();
- llvm::InitializeNativeTargetAsmPrinter();
-
- llvm::EngineBuilder builder(module.get());
- builder.setEngineKind(llvm::EngineKind::JIT);
- builder.setUseMCJIT(true);
- std::unique_ptr memoryManager(new llvm::SectionMemoryManager);
- builder.setMCJITMemoryManager(memoryManager.get());
- builder.setOptLevel(llvm::CodeGenOpt::None);
-
- auto triple = llvm::Triple(llvm::sys::getProcessTriple());
- if (triple.getOS() == llvm::Triple::OSType::Win32)
- triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
- module->setTargetTriple(triple.str());
-
- ee.reset(builder.create());
- if (!ee)
- return ReturnCode::LLVMConfigError;
-
- module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module
- memoryManager.release(); // and memory manager
-
- if (objectCache)
- ee->setObjectCache(objectCache);
- entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
- }
- else
{
- if (!entryFuncPtr)
- {
- ee->addModule(module.get());
- module.release();
- entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
- }
+ listener->stateChanged(ExecState::Compilation);
+ assert(_data->code || !_data->codeSize); //TODO: Is it good idea to execute empty code?
+ module = Compiler({}).compile(_data->code, _data->code + _data->codeSize, mainFuncName);
}
+ if (debugDumpModule)
+ module->dump();
+
+ ee->addModule(module.get());
+ module.release();
+ listener->stateChanged(ExecState::CodeGen);
+ entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
}
- assert(entryFuncPtr);
+ if (!CHECK(entryFuncPtr))
+ return ReturnCode::LLVMLinkError;
- auto executionStartTime = std::chrono::high_resolution_clock::now();
+ listener->stateChanged(ExecState::Execution);
+ auto returnCode = entryFuncPtr(&runtime);
+ listener->stateChanged(ExecState::Return);
- auto returnCode = runEntryFunc(entryFuncPtr, &runtime);
if (returnCode == ReturnCode::Return)
{
returnData = runtime.getReturnData(); // Save reference to return data
std::swap(m_memory, runtime.getMemory()); // Take ownership of memory
}
+ listener->stateChanged(ExecState::Finished);
- auto executionEndTime = std::chrono::high_resolution_clock::now();
- clog(JIT) << " + " << std::chrono::duration_cast(executionEndTime - executionStartTime).count() << " ms\n";
+ if (statsCollectingEnabled)
+ statsCollector.stats.push_back(std::move(listener));
return returnCode;
}
diff --git a/evmjit/libevmjit/ExecutionEngine.h b/evmjit/libevmjit/ExecutionEngine.h
index e8d1e1c05..4c2965e58 100644
--- a/evmjit/libevmjit/ExecutionEngine.h
+++ b/evmjit/libevmjit/ExecutionEngine.h
@@ -1,5 +1,7 @@
#pragma once
+#include
+
#include "RuntimeData.h"
namespace dev
@@ -9,14 +11,40 @@ namespace eth
namespace jit
{
+enum class ExecState
+{
+ Started,
+ CacheLoad,
+ CacheWrite,
+ Compilation,
+ CodeGen,
+ Execution,
+ Return,
+ Finished
+};
+
+class ExecutionEngineListener
+{
+public:
+ ExecutionEngineListener() = default;
+ ExecutionEngineListener(ExecutionEngineListener const&) = delete;
+ ExecutionEngineListener& operator=(ExecutionEngineListener) = delete;
+ virtual ~ExecutionEngineListener() {}
+
+ virtual void executionStarted() {}
+ virtual void executionEnded() {}
+
+ virtual void stateChanged(ExecState) {}
+};
+
class ExecutionEngine
{
public:
ExecutionEngine() = default;
ExecutionEngine(ExecutionEngine const&) = delete;
- void operator=(ExecutionEngine) = delete;
+ ExecutionEngine& operator=(ExecutionEngine) = delete;
- EXPORT ReturnCode run(bytes const& _code, RuntimeData* _data, Env* _env);
+ EXPORT ReturnCode run(RuntimeData* _data, Env* _env);
/// Reference to returned data (RETURN opcode used)
bytes_ref returnData;
diff --git a/evmjit/libevmjit/Ext.cpp b/evmjit/libevmjit/Ext.cpp
index a37a6fe99..38deef214 100644
--- a/evmjit/libevmjit/Ext.cpp
+++ b/evmjit/libevmjit/Ext.cpp
@@ -1,9 +1,8 @@
-
#include "Ext.h"
-#include
-#include
+#include "preprocessor/llvm_includes_start.h"
#include
+#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Memory.h"
@@ -41,8 +40,8 @@ std::array::value> const& getEnvFuncDescs()
FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
- FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})},
- FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})},
+ FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})},
+ FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
@@ -60,14 +59,16 @@ llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module)
llvm::Value* Ext::getArgAlloca()
{
- auto& a = m_argAllocas[m_argCounter++];
+ auto& a = m_argAllocas[m_argCounter];
if (!a)
{
- // FIXME: Improve order and names
InsertPointGuard g{getBuilder()};
- getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI());
- a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg");
+ auto allocaIt = getMainFunction()->front().begin();
+ std::advance(allocaIt, m_argCounter); // Skip already created allocas
+ getBuilder().SetInsertPoint(allocaIt);
+ a = getBuilder().CreateAlloca(Type::Word, nullptr, {"a.", std::to_string(m_argCounter)});
}
+ ++m_argCounter;
return a;
}
@@ -124,30 +125,26 @@ llvm::Value* Ext::blockhash(llvm::Value* _number)
return Endianness::toNative(getBuilder(), hash);
}
-llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize)
+llvm::Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize)
{
- auto gas = byPtr(_gas);
auto ret = getArgAlloca();
auto begin = m_memoryMan.getBytePtr(_initOff);
auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size");
- createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret});
- _gas = m_builder.CreateLoad(gas); // Return gas
+ createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(_endowment), begin, size, ret});
llvm::Value* address = m_builder.CreateLoad(ret);
address = Endianness::toNative(m_builder, address);
return address;
}
-llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress)
+llvm::Value* Ext::call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress)
{
- auto gas = byPtr(_gas);
auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress);
auto inBeg = m_memoryMan.getBytePtr(_inOff);
auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size");
auto outBeg = m_memoryMan.getBytePtr(_outOff);
auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size");
auto codeAddress = Endianness::toBE(m_builder, _codeAddress);
- auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), gas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)});
- _gas = m_builder.CreateLoad(gas); // Return gas
+ auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)});
return m_builder.CreateZExt(ret, Type::Word, "ret");
}
diff --git a/evmjit/libevmjit/Ext.h b/evmjit/libevmjit/Ext.h
index 2601d32ab..1c0c0fc56 100644
--- a/evmjit/libevmjit/Ext.h
+++ b/evmjit/libevmjit/Ext.h
@@ -1,7 +1,7 @@
-
#pragma once
#include
+
#include "CompilerHelper.h"
namespace dev
@@ -50,8 +50,8 @@ public:
llvm::Value* balance(llvm::Value* _address);
llvm::Value* calldataload(llvm::Value* _index);
- llvm::Value* create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize);
- llvm::Value* call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress);
+ llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize);
+ llvm::Value* call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress);
llvm::Value* blockhash(llvm::Value* _number);
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize);
diff --git a/evmjit/libevmjit/GasMeter.cpp b/evmjit/libevmjit/GasMeter.cpp
index 4aa6a738d..ca21714e0 100644
--- a/evmjit/libevmjit/GasMeter.cpp
+++ b/evmjit/libevmjit/GasMeter.cpp
@@ -1,11 +1,9 @@
-
#include "GasMeter.h"
-#include
-#include
+#include "preprocessor/llvm_includes_start.h"
#include
+#include "preprocessor/llvm_includes_end.h"
-#include "Type.h"
#include "Ext.h"
#include "RuntimeManager.h"
@@ -19,29 +17,29 @@ namespace jit
namespace // Helper functions
{
-uint64_t const c_stepGas = 1;
-uint64_t const c_balanceGas = 20;
-uint64_t const c_sha3Gas = 10;
-uint64_t const c_sha3WordGas = 10;
-uint64_t const c_sloadGas = 20;
-uint64_t const c_sstoreSetGas = 300;
-uint64_t const c_sstoreResetGas = 100;
-uint64_t const c_sstoreRefundGas = 100;
-uint64_t const c_createGas = 100;
-uint64_t const c_createDataGas = 5;
-uint64_t const c_callGas = 20;
-uint64_t const c_expGas = 1;
-uint64_t const c_expByteGas = 1;
-uint64_t const c_memoryGas = 1;
-uint64_t const c_txDataZeroGas = 1;
-uint64_t const c_txDataNonZeroGas = 5;
-uint64_t const c_txGas = 500;
-uint64_t const c_logGas = 32;
-uint64_t const c_logDataGas = 1;
-uint64_t const c_logTopicGas = 32;
-uint64_t const c_copyGas = 1;
-
-uint64_t getStepCost(Instruction inst)
+int64_t const c_stepGas = 1;
+int64_t const c_balanceGas = 20;
+int64_t const c_sha3Gas = 10;
+int64_t const c_sha3WordGas = 10;
+int64_t const c_sloadGas = 20;
+int64_t const c_sstoreSetGas = 300;
+int64_t const c_sstoreResetGas = 100;
+int64_t const c_sstoreRefundGas = 100;
+int64_t const c_createGas = 100;
+int64_t const c_createDataGas = 5;
+int64_t const c_callGas = 20;
+int64_t const c_expGas = 1;
+int64_t const c_expByteGas = 1;
+int64_t const c_memoryGas = 1;
+int64_t const c_txDataZeroGas = 1;
+int64_t const c_txDataNonZeroGas = 5;
+int64_t const c_txGas = 500;
+int64_t const c_logGas = 32;
+int64_t const c_logDataGas = 1;
+int64_t const c_logTopicGas = 32;
+int64_t const c_copyGas = 1;
+
+int64_t getStepCost(Instruction inst)
{
switch (inst)
{
@@ -72,7 +70,7 @@ uint64_t getStepCost(Instruction inst)
case Instruction::LOG3:
case Instruction::LOG4:
{
- auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0);
+ auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0);
return c_logGas + numTopics * c_logTopicGas;
}
}
@@ -86,7 +84,7 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
{
auto module = getModule();
- llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Word};
+ llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Gas};
m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", module);
InsertPointGuard guard(m_builder);
@@ -94,22 +92,22 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc);
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc);
+ auto rt = &m_gasCheckFunc->getArgumentList().front();
+ rt->setName("rt");
+ auto cost = rt->getNextNode();
+ cost->setName("cost");
+
m_builder.SetInsertPoint(checkBB);
- auto arg = m_gasCheckFunc->arg_begin();
- arg->setName("rt");
- ++arg;
- arg->setName("cost");
- auto cost = arg;
auto gas = m_runtimeManager.getGas();
- auto isOutOfGas = m_builder.CreateICmpUGT(cost, gas, "isOutOfGas");
+ gas = m_builder.CreateNSWSub(gas, cost, "gasUpdated");
+ auto isOutOfGas = m_builder.CreateICmpSLT(gas, m_builder.getInt64(0), "isOutOfGas"); // gas < 0, with gas == 0 we can still do 0 cost instructions
m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB);
m_builder.SetInsertPoint(outOfGasBB);
- m_runtimeManager.raiseException(ReturnCode::OutOfGas);
+ m_runtimeManager.abort();
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(updateBB);
- gas = m_builder.CreateSub(gas, cost);
m_runtimeManager.setGas(gas);
m_builder.CreateRetVoid();
}
@@ -119,7 +117,7 @@ void GasMeter::count(Instruction _inst)
if (!m_checkCall)
{
// Create gas check call with mocked block cost at begining of current cost-block
- m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word)});
+ m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Gas)});
}
m_blockCost += getStepCost(_inst);
@@ -127,6 +125,15 @@ void GasMeter::count(Instruction _inst)
void GasMeter::count(llvm::Value* _cost)
{
+ if (_cost->getType() == Type::Word)
+ {
+ auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word);
+ auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh");
+ auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas);
+ _cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost");
+ }
+
+ assert(_cost->getType() == Type::Gas);
createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost});
}
@@ -136,12 +143,13 @@ void GasMeter::countExp(llvm::Value* _exponent)
// lz - leading zeros
// cost = ((256 - lz) + 7) / 8
- // OPT: All calculations can be done on 32/64 bits
+ // OPT: Can gas update be done in exp algorithm?
auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
- auto lz = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false));
- auto sigBits = m_builder.CreateSub(Constant::get(256), lz);
- auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, Constant::get(7)), Constant::get(8));
+ auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false));
+ auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
+ auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits");
+ auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8));
count(sigBytes);
}
@@ -154,8 +162,8 @@ void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValu
auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero");
auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert");
auto isDelete = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDelete");
- auto cost = m_builder.CreateSelect(isInsert, Constant::get(c_sstoreSetGas), Constant::get(c_sstoreResetGas), "cost");
- cost = m_builder.CreateSelect(isDelete, Constant::get(0), cost, "cost");
+ auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(c_sstoreSetGas), m_builder.getInt64(c_sstoreResetGas), "cost");
+ cost = m_builder.CreateSelect(isDelete, m_builder.getInt64(0), cost, "cost");
count(cost);
}
@@ -173,17 +181,16 @@ void GasMeter::countSha3Data(llvm::Value* _dataLength)
assert(m_blockCost > 0); // SHA3 instruction is already counted
// TODO: This round ups to 32 happens in many places
- // FIXME: 64-bit arith used, but not verified
static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter");
- auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::lowPrecision);
- auto words64 = m_builder.CreateUDiv(m_builder.CreateAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32));
+ auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::Gas);
+ auto words64 = m_builder.CreateUDiv(m_builder.CreateNUWAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32));
auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64);
- auto cost = getBuilder().CreateZExt(cost64, Type::Word);
- count(cost);
+ count(cost64);
}
void GasMeter::giveBack(llvm::Value* _gas)
{
+ assert(_gas->getType() == Type::Gas);
m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas));
}
@@ -199,7 +206,7 @@ void GasMeter::commitCostBlock()
return;
}
- m_checkCall->setArgOperand(1, Constant::get(m_blockCost)); // Update block cost in gas check call
+ m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call
m_checkCall = nullptr; // End cost-block
m_blockCost = 0;
}
diff --git a/evmjit/libevmjit/GasMeter.h b/evmjit/libevmjit/GasMeter.h
index 56da6eb9f..4056cd64d 100644
--- a/evmjit/libevmjit/GasMeter.h
+++ b/evmjit/libevmjit/GasMeter.h
@@ -1,4 +1,3 @@
-
#pragma once
#include "CompilerHelper.h"
@@ -50,7 +49,7 @@ public:
private:
/// Cumulative gas cost of a block of instructions
/// @TODO Handle overflow
- uint64_t m_blockCost = 0;
+ int64_t m_blockCost = 0;
llvm::CallInst* m_checkCall = nullptr;
llvm::Function* m_gasCheckFunc = nullptr;
diff --git a/evmjit/libevmjit/Instruction.cpp b/evmjit/libevmjit/Instruction.cpp
index fdc40d043..f70b020f8 100644
--- a/evmjit/libevmjit/Instruction.cpp
+++ b/evmjit/libevmjit/Instruction.cpp
@@ -1,6 +1,8 @@
-
#include "Instruction.h"
+
+#include "preprocessor/llvm_includes_start.h"
#include
+#include "preprocessor/llvm_includes_end.h"
namespace dev
{
@@ -9,7 +11,7 @@ namespace eth
namespace jit
{
-llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end)
+llvm::APInt readPushData(code_iterator& _curr, code_iterator _end)
{
auto pushInst = *_curr;
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32);
@@ -26,7 +28,7 @@ llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _en
return value;
}
-void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end)
+void skipPushData(code_iterator& _curr, code_iterator _end)
{
auto pushInst = *_curr;
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32);
diff --git a/evmjit/libevmjit/Instruction.h b/evmjit/libevmjit/Instruction.h
index 158490dee..6785213d6 100644
--- a/evmjit/libevmjit/Instruction.h
+++ b/evmjit/libevmjit/Instruction.h
@@ -161,11 +161,11 @@ enum class Instruction: uint8_t
/// Reads PUSH data from pointed fragment of bytecode and constructs number out of it
/// Reading out of bytecode means reading 0
/// @param _curr is updated and points the last real byte read
-llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end);
+llvm::APInt readPushData(code_iterator& _curr, code_iterator _end);
/// Skips PUSH data in pointed fragment of bytecode.
/// @param _curr is updated and points the last real byte skipped
-void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end);
+void skipPushData(code_iterator& _curr, code_iterator _end);
#define ANY_PUSH PUSH1: \
case Instruction::PUSH2: \
diff --git a/evmjit/libevmjit/Memory.cpp b/evmjit/libevmjit/Memory.cpp
index c6f8a9ec0..647c5f26a 100644
--- a/evmjit/libevmjit/Memory.cpp
+++ b/evmjit/libevmjit/Memory.cpp
@@ -1,239 +1,260 @@
-#include "Memory.h"
-
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#include "Type.h"
-#include "Runtime.h"
-#include "GasMeter.h"
-#include "Endianness.h"
-#include "RuntimeManager.h"
-
-namespace dev
-{
-namespace eth
-{
-namespace jit
-{
-
-Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
- RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
- m_gasMeter(_gasMeter)
-{
- llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
- m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule());
- llvm::AttrBuilder attrBuilder;
- attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly);
- m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder));
-
- m_require = createRequireFunc(_gasMeter);
- m_loadWord = createFunc(false, Type::Word, _gasMeter);
- m_storeWord = createFunc(true, Type::Word, _gasMeter);
- m_storeByte = createFunc(true, Type::Byte, _gasMeter);
-}
-
-llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
-{
- llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word};
- auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule());
- auto rt = func->arg_begin();
- rt->setName("rt");
- auto offset = rt->getNextNode();
- offset->setName("offset");
- auto size = offset->getNextNode();
- size->setName("size");
-
- auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func);
- auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
- auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
- auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func);
-
- InsertPointGuard guard(m_builder); // Restores insert point at function exit
-
- // BB "Pre": Ignore checks with size 0
- m_builder.SetInsertPoint(preBB);
- auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0));
- m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB);
-
- // BB "Check"
- m_builder.SetInsertPoint(checkBB);
- auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word);
- auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res");
- auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq");
- auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1");
- auto rtPtr = getRuntimeManager().getRuntimePtr();
- auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
- auto currSize = m_builder.CreateLoad(sizePtr, "currSize");
- auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall");
- auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded");
- m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
-
- // BB "Resize"
- m_builder.SetInsertPoint(resizeBB);
- // Check gas first
- uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res");
- auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0);
- auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2");
- auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow");
- wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired);
- wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq");
- sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq");
- auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k
- auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
- _gasMeter.countMemory(newWords);
- // Resize
- m_builder.CreateStore(sizeRequired, sizePtr);
- auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData");
- auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
- m_builder.CreateStore(newData, dataPtr);
- m_builder.CreateBr(returnBB);
-
- // BB "Return"
- m_builder.SetInsertPoint(returnBB);
- m_builder.CreateRetVoid();
- return func;
-}
-
-llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&)
-{
- auto isWord = _valueType == Type::Word;
-
- llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType};
- llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word};
- auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload";
- auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false);
- auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule());
-
- InsertPointGuard guard(m_builder); // Restores insert point at function exit
-
- m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func));
- auto rt = func->arg_begin();
- rt->setName("rt");
- auto index = rt->getNextNode();
- index->setName("index");
-
- auto valueSize = _valueType->getPrimitiveSizeInBits() / 8;
- this->require(index, Constant::get(valueSize));
- auto ptr = getBytePtr(index);
- if (isWord)
- ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr");
- if (_isStore)
- {
- llvm::Value* value = index->getNextNode();
- value->setName("value");
- if (isWord)
- value = Endianness::toBE(m_builder, value);
- m_builder.CreateStore(value, ptr);
- m_builder.CreateRetVoid();
- }
- else
- {
- llvm::Value* ret = m_builder.CreateLoad(ptr);
- ret = Endianness::toNative(m_builder, ret);
- m_builder.CreateRet(ret);
- }
-
- return func;
-}
-
-
-llvm::Value* Memory::loadWord(llvm::Value* _addr)
-{
- return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr});
-}
-
-void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
-{
- createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word});
-}
-
-void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
-{
- auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
- createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte});
-}
-
-llvm::Value* Memory::getData()
-{
- auto rtPtr = getRuntimeManager().getRuntimePtr();
- auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
- return m_builder.CreateLoad(dataPtr, "data");
-}
-
-llvm::Value* Memory::getSize()
-{
- auto rtPtr = getRuntimeManager().getRuntimePtr();
- auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
- return m_builder.CreateLoad(sizePtr, "size");
-}
-
-llvm::Value* Memory::getBytePtr(llvm::Value* _index)
-{
- auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64
- return m_builder.CreateGEP(getData(), idx, "ptr");
-}
-
-void Memory::require(llvm::Value* _offset, llvm::Value* _size)
-{
- createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size});
-}
-
-void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
- llvm::Value* _destMemIdx, llvm::Value* _reqBytes)
-{
- require(_destMemIdx, _reqBytes);
-
- // Additional copy cost
- // TODO: This round ups to 32 happens in many places
- auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32));
- m_gasMeter.countCopy(copyWords);
-
- // Algorithm:
- // isOutsideData = idx256 >= size256
- // idx64 = trunc idx256
- // size64 = trunc size256
- // dataLeftSize = size64 - idx64 // safe if not isOutsideData
- // reqBytes64 = trunc _reqBytes // require() handles large values
- // bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
- // bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
-
- auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize);
- auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision);
- auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision);
- auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
- auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision);
- auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize);
- auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64);
- auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants
- auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner);
-
- auto src = m_builder.CreateGEP(_srcPtr, idx64, "src");
- auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64
- auto dst = m_builder.CreateGEP(getData(), dstIdx, "dst");
- m_builder.CreateMemCpy(dst, src, bytesToCopy, 0);
-}
-
-}
-}
-}
-
-
-extern "C"
-{
- using namespace dev::eth::jit;
-
- EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
- {
- auto size = _size->a; // Trunc to 64-bit
- auto& memory = _rt->getMemory();
- memory.resize(size);
- return memory.data();
- }
-}
+#include "Memory.h"
+
+#include "preprocessor/llvm_includes_start.h"
+#include
+#include "preprocessor/llvm_includes_end.h"
+
+#include "Type.h"
+#include "Runtime.h"
+#include "GasMeter.h"
+#include "Endianness.h"
+#include "RuntimeManager.h"
+
+namespace dev
+{
+namespace eth
+{
+namespace jit
+{
+
+Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
+ RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
+ m_gasMeter(_gasMeter)
+{}
+
+llvm::Function* Memory::getRequireFunc()
+{
+ auto& func = m_require;
+ if (!func)
+ {
+ llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word};
+ func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule());
+ auto rt = func->arg_begin();
+ rt->setName("rt");
+ auto offset = rt->getNextNode();
+ offset->setName("offset");
+ auto size = offset->getNextNode();
+ size->setName("size");
+
+ llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
+ auto resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule());
+ llvm::AttrBuilder attrBuilder;
+ attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly);
+ resize->setAttributes(llvm::AttributeSet::get(resize->getContext(), 1, attrBuilder));
+
+ auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func);
+ auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
+ auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
+ auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func);
+
+ InsertPointGuard guard(m_builder); // Restores insert point at function exit
+
+ // BB "Pre": Ignore checks with size 0
+ m_builder.SetInsertPoint(preBB);
+ auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0));
+ m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB);
+
+ // BB "Check"
+ m_builder.SetInsertPoint(checkBB);
+ auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word);
+ auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res");
+ auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq");
+ auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1");
+ auto rtPtr = getRuntimeManager().getRuntimePtr();
+ auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
+ auto currSize = m_builder.CreateLoad(sizePtr, "currSize");
+ auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall");
+ auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded");
+ m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
+
+ // BB "Resize"
+ m_builder.SetInsertPoint(resizeBB);
+ // Check gas first
+ uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res");
+ auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0);
+ auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2");
+ auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow");
+ wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired);
+ wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq");
+ sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq");
+ auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k
+ auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
+ m_gasMeter.countMemory(newWords);
+ // Resize
+ m_builder.CreateStore(sizeRequired, sizePtr);
+ auto newData = m_builder.CreateCall2(resize, rt, sizePtr, "newData");
+ auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
+ m_builder.CreateStore(newData, dataPtr);
+ m_builder.CreateBr(returnBB);
+
+ // BB "Return"
+ m_builder.SetInsertPoint(returnBB);
+ m_builder.CreateRetVoid();
+ }
+ return func;
+}
+
+llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&)
+{
+ auto isWord = _valueType == Type::Word;
+
+ llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType};
+ llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word};
+ auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload";
+ auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false);
+ auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule());
+
+ InsertPointGuard guard(m_builder); // Restores insert point at function exit
+
+ m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func));
+ auto rt = func->arg_begin();
+ rt->setName("rt");
+ auto index = rt->getNextNode();
+ index->setName("index");
+
+ auto valueSize = _valueType->getPrimitiveSizeInBits() / 8;
+ this->require(index, Constant::get(valueSize));
+ auto ptr = getBytePtr(index);
+ if (isWord)
+ ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr");
+ if (_isStore)
+ {
+ llvm::Value* value = index->getNextNode();
+ value->setName("value");
+ if (isWord)
+ value = Endianness::toBE(m_builder, value);
+ m_builder.CreateStore(value, ptr);
+ m_builder.CreateRetVoid();
+ }
+ else
+ {
+ llvm::Value* ret = m_builder.CreateLoad(ptr);
+ ret = Endianness::toNative(m_builder, ret);
+ m_builder.CreateRet(ret);
+ }
+
+ return func;
+}
+
+llvm::Function* Memory::getLoadWordFunc()
+{
+ auto& func = m_loadWord;
+ if (!func)
+ func = createFunc(false, Type::Word, m_gasMeter);
+ return func;
+}
+
+llvm::Function* Memory::getStoreWordFunc()
+{
+ auto& func = m_storeWord;
+ if (!func)
+ func = createFunc(true, Type::Word, m_gasMeter);
+ return func;
+}
+
+llvm::Function* Memory::getStoreByteFunc()
+{
+ auto& func = m_storeByte;
+ if (!func)
+ func = createFunc(true, Type::Byte, m_gasMeter);
+ return func;
+}
+
+
+llvm::Value* Memory::loadWord(llvm::Value* _addr)
+{
+ return createCall(getLoadWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr});
+}
+
+void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
+{
+ createCall(getStoreWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr, _word});
+}
+
+void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
+{
+ auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
+ createCall(getStoreByteFunc(), {getRuntimeManager().getRuntimePtr(), _addr, byte});
+}
+
+llvm::Value* Memory::getData()
+{
+ auto rtPtr = getRuntimeManager().getRuntimePtr();
+ auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
+ return m_builder.CreateLoad(dataPtr, "data");
+}
+
+llvm::Value* Memory::getSize()
+{
+ auto rtPtr = getRuntimeManager().getRuntimePtr();
+ auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
+ return m_builder.CreateLoad(sizePtr, "size");
+}
+
+llvm::Value* Memory::getBytePtr(llvm::Value* _index)
+{
+ auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64
+ return m_builder.CreateGEP(getData(), idx, "ptr");
+}
+
+void Memory::require(llvm::Value* _offset, llvm::Value* _size)
+{
+ if (auto constant = llvm::dyn_cast(_size))
+ {
+ if (!constant->getValue())
+ return;
+ }
+ createCall(getRequireFunc(), {getRuntimeManager().getRuntimePtr(), _offset, _size});
+}
+
+void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
+ llvm::Value* _destMemIdx, llvm::Value* _reqBytes)
+{
+ require(_destMemIdx, _reqBytes);
+
+ // Additional copy cost
+ // TODO: This round ups to 32 happens in many places
+ auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Gas);
+ auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32));
+ m_gasMeter.countCopy(copyWords);
+
+ // Algorithm:
+ // isOutsideData = idx256 >= size256
+ // idx64 = trunc idx256
+ // size64 = trunc size256
+ // dataLeftSize = size64 - idx64 // safe if not isOutsideData
+ // reqBytes64 = trunc _reqBytes // require() handles large values
+ // bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
+ // bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
+
+ auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize);
+ auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size);
+ auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size);
+ auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
+ auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize);
+ auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes);
+ auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner);
+
+ auto src = m_builder.CreateGEP(_srcPtr, idx64, "src");
+ auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64
+ auto dst = m_builder.CreateGEP(getData(), dstIdx, "dst");
+ m_builder.CreateMemCpy(dst, src, bytesToCopy, 0);
+}
+
+}
+}
+}
+
+
+extern "C"
+{
+ using namespace dev::eth::jit;
+
+ EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
+ {
+ auto size = _size->a; // Trunc to 64-bit
+ auto& memory = _rt->getMemory();
+ memory.resize(size);
+ return memory.data();
+ }
+}
diff --git a/evmjit/libevmjit/Memory.h b/evmjit/libevmjit/Memory.h
index ed9c51805..e8edce735 100644
--- a/evmjit/libevmjit/Memory.h
+++ b/evmjit/libevmjit/Memory.h
@@ -31,13 +31,16 @@ private:
GasMeter& m_gasMeter;
llvm::Function* createFunc(bool _isStore, llvm::Type* _type, GasMeter& _gasMeter);
- llvm::Function* createRequireFunc(GasMeter& _gasMeter);
- llvm::Function* m_resize;
- llvm::Function* m_require;
- llvm::Function* m_loadWord;
- llvm::Function* m_storeWord;
- llvm::Function* m_storeByte;
+ llvm::Function* getRequireFunc();
+ llvm::Function* getLoadWordFunc();
+ llvm::Function* getStoreWordFunc();
+ llvm::Function* getStoreByteFunc();
+
+ llvm::Function* m_require = nullptr;
+ llvm::Function* m_loadWord = nullptr;
+ llvm::Function* m_storeWord = nullptr;
+ llvm::Function* m_storeByte = nullptr;
};
}
diff --git a/evmjit/libevmjit/Runtime.cpp b/evmjit/libevmjit/Runtime.cpp
index eb70e01d1..69937368c 100644
--- a/evmjit/libevmjit/Runtime.cpp
+++ b/evmjit/libevmjit/Runtime.cpp
@@ -1,9 +1,6 @@
-
#include "Runtime.h"
-#include
-#include
-#include
+#include
namespace dev
{
@@ -14,8 +11,7 @@ namespace jit
Runtime::Runtime(RuntimeData* _data, Env* _env) :
m_data(*_data),
- m_env(*_env),
- m_currJmpBuf(m_jmpBuf)
+ m_env(*_env)
{}
bytes_ref Runtime::getReturnData() const
diff --git a/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h
index 20cf56984..82be4a0c8 100644
--- a/evmjit/libevmjit/Runtime.h
+++ b/evmjit/libevmjit/Runtime.h
@@ -1,46 +1,40 @@
-
-#pragma once
-
-#include
-#include "RuntimeData.h"
-
-namespace dev
-{
-namespace eth
-{
-namespace jit
-{
-
-using StackImpl = std::vector;
-using MemoryImpl = bytes;
-using jmp_buf_ref = decltype(&std::jmp_buf{}[0]);
-
-class Runtime
-{
-public:
- Runtime(RuntimeData* _data, Env* _env);
-
- Runtime(const Runtime&) = delete;
- Runtime& operator=(const Runtime&) = delete;
-
- StackImpl& getStack() { return m_stack; }
- MemoryImpl& getMemory() { return m_memory; }
- Env* getEnvPtr() { return &m_env; }
-
- bytes_ref getReturnData() const;
- jmp_buf_ref getJmpBuf() { return m_jmpBuf; }
-
-private:
- RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
- Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract.
- jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract.
- byte* m_memoryData = nullptr;
- i256 m_memorySize;
- std::jmp_buf m_jmpBuf;
- StackImpl m_stack;
- MemoryImpl m_memory;
-};
-
-}
-}
-}
+#pragma once
+
+#include "RuntimeData.h"
+
+namespace dev
+{
+namespace eth
+{
+namespace jit
+{
+
+using StackImpl = std::vector;
+using MemoryImpl = bytes;
+
+class Runtime
+{
+public:
+ Runtime(RuntimeData* _data, Env* _env);
+
+ Runtime(const Runtime&) = delete;
+ Runtime& operator=(const Runtime&) = delete;
+
+ StackImpl& getStack() { return m_stack; }
+ MemoryImpl& getMemory() { return m_memory; }
+
+ bytes_ref getReturnData() const;
+
+private:
+ RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
+ Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract.
+ 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/RuntimeData.h b/evmjit/libevmjit/RuntimeData.h
index 58d68db8a..cc081cc58 100644
--- a/evmjit/libevmjit/RuntimeData.h
+++ b/evmjit/libevmjit/RuntimeData.h
@@ -1,7 +1,6 @@
#pragma once
-#include "Utils.h"
-
+#include "Common.h"
namespace dev
{
@@ -50,6 +49,7 @@ struct RuntimeData
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
+ i256 codeHash;
};
/// VM Environment (ExtVM) opaque type
diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp
index 408f2dee3..0b5762fa2 100644
--- a/evmjit/libevmjit/RuntimeManager.cpp
+++ b/evmjit/libevmjit/RuntimeManager.cpp
@@ -1,12 +1,8 @@
-
#include "RuntimeManager.h"
-#include
-#include
-#include
-
-#include "RuntimeData.h"
-#include "Instruction.h"
+#include "preprocessor/llvm_includes_start.h"
+#include
+#include "preprocessor/llvm_includes_end.h"
namespace dev
{
@@ -87,9 +83,17 @@ llvm::Twine getName(RuntimeData::Index _index)
}
}
-RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder): CompilerHelper(_builder)
+RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd):
+ CompilerHelper(_builder),
+ m_jmpBuf(_jmpBuf),
+ m_codeBegin(_codeBegin),
+ m_codeEnd(_codeEnd)
{
- m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::longjmp);
+ m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
+
+ // save jmpBuf to be used in helper functions
+ auto ptr = m_builder.CreateStructGEP(getRuntimePtr(), 2);
+ m_builder.CreateStore(m_jmpBuf, ptr, "jmpBufExt");
// Unpack data
auto rtPtr = getRuntimePtr();
@@ -161,9 +165,10 @@ void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress)
set(RuntimeData::SuicideDestAddress, _balanceAddress);
}
-void RuntimeManager::raiseException(ReturnCode _returnCode)
+void RuntimeManager::abort(llvm::Value* _jmpBuf)
{
- m_builder.CreateCall2(m_longjmp, getJmpBuf(), Constant::get(_returnCode));
+ auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
+ createCall(longjmp, {_jmpBuf});
}
llvm::Value* RuntimeManager::get(Instruction _inst)
@@ -191,14 +196,14 @@ llvm::Value* RuntimeManager::getCallData()
llvm::Value* RuntimeManager::getCode()
{
- return get(RuntimeData::Code);
+ // OPT Check what is faster
+ //return get(RuntimeData::Code);
+ return m_builder.CreateGlobalStringPtr({reinterpret_cast(m_codeBegin), static_cast(m_codeEnd - m_codeBegin)}, "code");
}
llvm::Value* RuntimeManager::getCodeSize()
{
- auto value = get(RuntimeData::CodeSize);
- assert(value->getType() == Type::Size);
- return getBuilder().CreateZExt(value, Type::Word);
+ return Constant::get(m_codeEnd - m_codeBegin);
}
llvm::Value* RuntimeManager::getCallDataSize()
@@ -208,23 +213,28 @@ llvm::Value* RuntimeManager::getCallDataSize()
return getBuilder().CreateZExt(value, Type::Word);
}
-llvm::Value* RuntimeManager::getJmpBuf()
+llvm::Value* RuntimeManager::getJmpBufExt()
{
- auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2, "jmpbufPtr");
- return getBuilder().CreateLoad(ptr, "jmpbuf");
+ auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2);
+ return getBuilder().CreateLoad(ptr, "jmpBufExt");
}
llvm::Value* RuntimeManager::getGas()
{
- auto value = get(RuntimeData::Gas);
- assert(value->getType() == Type::Size);
- return getBuilder().CreateZExt(value, Type::Word);
+ auto gas = get(RuntimeData::Gas);
+ assert(gas->getType() == Type::Gas);
+ return gas;
+}
+
+llvm::Value* RuntimeManager::getGasPtr()
+{
+ return getPtr(RuntimeData::Gas);
}
void RuntimeManager::setGas(llvm::Value* _gas)
{
- auto newGas = getBuilder().CreateTrunc(_gas, Type::Size);
- set(RuntimeData::Gas, newGas);
+ assert(_gas->getType() == Type::Gas);
+ set(RuntimeData::Gas, _gas);
}
}
diff --git a/evmjit/libevmjit/RuntimeManager.h b/evmjit/libevmjit/RuntimeManager.h
index b5f3ca657..30c69ec88 100644
--- a/evmjit/libevmjit/RuntimeManager.h
+++ b/evmjit/libevmjit/RuntimeManager.h
@@ -15,25 +15,28 @@ namespace jit
class RuntimeManager: public CompilerHelper
{
public:
- RuntimeManager(llvm::IRBuilder<>& _builder);
+ RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd);
llvm::Value* getRuntimePtr();
llvm::Value* getDataPtr();
- llvm::Value* getEnvPtr(); // TODO: Can we make it const?
+ llvm::Value* getEnvPtr();
llvm::Value* get(RuntimeData::Index _index);
llvm::Value* get(Instruction _inst);
- llvm::Value* getGas(); // TODO: Remove
+ llvm::Value* getGas();
+ llvm::Value* getGasPtr();
llvm::Value* getCallData();
llvm::Value* getCode();
llvm::Value* getCodeSize();
llvm::Value* getCallDataSize();
+ llvm::Value* getJmpBuf() { return m_jmpBuf; }
void setGas(llvm::Value* _gas);
void registerReturnData(llvm::Value* _index, llvm::Value* _size);
void registerSuicide(llvm::Value* _balanceAddress);
- void raiseException(ReturnCode _returnCode);
+ void abort(llvm::Value* _jmpBuf);
+ void abort() { abort(getJmpBufExt()); }
static llvm::StructType* getRuntimeType();
static llvm::StructType* getRuntimeDataType();
@@ -41,11 +44,15 @@ public:
private:
llvm::Value* getPtr(RuntimeData::Index _index);
void set(RuntimeData::Index _index, llvm::Value* _value);
- llvm::Value* getJmpBuf();
+ llvm::Value* getJmpBufExt();
llvm::Function* m_longjmp = nullptr;
+ llvm::Value* const m_jmpBuf;
llvm::Value* m_dataPtr = nullptr;
llvm::Value* m_envPtr = nullptr;
+
+ code_iterator m_codeBegin = {};
+ code_iterator m_codeEnd = {};
};
}
diff --git a/evmjit/libevmjit/Stack.cpp b/evmjit/libevmjit/Stack.cpp
index 44f1c21c1..81a954991 100644
--- a/evmjit/libevmjit/Stack.cpp
+++ b/evmjit/libevmjit/Stack.cpp
@@ -1,10 +1,11 @@
#include "Stack.h"
-#include "RuntimeManager.h"
-#include "Runtime.h"
-#include "Type.h"
+#include "preprocessor/llvm_includes_start.h"
#include
-#include
+#include "preprocessor/llvm_includes_end.h"
+
+#include "RuntimeManager.h"
+#include "Runtime.h"
namespace dev
{
@@ -27,18 +28,87 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
llvm::Type* pushArgTypes[] = {Type::RuntimePtr, Type::WordPtr};
m_push = Function::Create(FunctionType::get(Type::Void, pushArgTypes, false), Linkage::ExternalLinkage, "stack_push", module);
- llvm::Type* popArgTypes[] = {Type::RuntimePtr, Type::Size};
- m_pop = Function::Create(FunctionType::get(Type::Void, popArgTypes, false), Linkage::ExternalLinkage, "stack_pop", module);
-
llvm::Type* getSetArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr};
- m_get = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_get", module);
m_set = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_set", module);
}
+llvm::Function* Stack::getPopFunc()
+{
+ auto& func = m_pop;
+ if (!func)
+ {
+ llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr};
+ func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.pop", getModule());
+ llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size};
+ auto extPopFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_pop", getModule());
+
+ auto rt = &func->getArgumentList().front();
+ rt->setName("rt");
+ auto index = rt->getNextNode();
+ index->setName("index");
+ auto jmpBuf = index->getNextNode();
+ jmpBuf->setName("jmpBuf");
+
+ InsertPointGuard guard{m_builder};
+ auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
+ auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func);
+ auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
+
+ m_builder.SetInsertPoint(entryBB);
+ auto ok = createCall(extPopFunc, {rt, index});
+ m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight
+
+ m_builder.SetInsertPoint(underflowBB);
+ m_runtimeManager.abort(jmpBuf);
+ m_builder.CreateUnreachable();
+
+ m_builder.SetInsertPoint(returnBB);
+ m_builder.CreateRetVoid();
+ }
+ return func;
+}
+
+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());
+
+ auto rt = &func->getArgumentList().front();
+ rt->setName("rt");
+ auto index = rt->getNextNode();
+ index->setName("index");
+ auto jmpBuf = index->getNextNode();
+ jmpBuf->setName("jmpBuf");
+
+ InsertPointGuard guard{m_builder};
+ auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
+ auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func);
+ 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
+
+ m_builder.SetInsertPoint(underflowBB);
+ m_runtimeManager.abort(jmpBuf);
+ m_builder.CreateUnreachable();
+
+ m_builder.SetInsertPoint(returnBB);
+ m_builder.CreateRet(valuePtr);
+ }
+ return func;
+}
+
llvm::Value* Stack::get(size_t _index)
{
- m_builder.CreateCall3(m_get, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _index, false), m_arg);
- return m_builder.CreateLoad(m_arg);
+ auto valuePtr = createCall(getGetFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_index), m_runtimeManager.getJmpBuf()});
+ return m_builder.CreateLoad(valuePtr);
}
void Stack::set(size_t _index, llvm::Value* _value)
@@ -49,7 +119,7 @@ void Stack::set(size_t _index, llvm::Value* _value)
void Stack::pop(size_t _count)
{
- m_builder.CreateCall2(m_pop, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _count, false));
+ createCall(getPopFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_count), m_runtimeManager.getJmpBuf()});
}
void Stack::push(llvm::Value* _value)
@@ -69,13 +139,14 @@ extern "C"
{
using namespace dev::eth::jit;
- EXPORT void stack_pop(Runtime* _rt, uint64_t _count)
+ EXPORT bool stack_pop(Runtime* _rt, uint64_t _count)
{
auto& stack = _rt->getStack();
if (stack.size() < _count)
- longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall));
+ return false;
stack.erase(stack.end() - _count, stack.end());
+ return true;
}
EXPORT void stack_push(Runtime* _rt, i256 const* _word)
@@ -87,22 +158,18 @@ extern "C"
Stack::maxStackSize = stack.size();
}
- EXPORT void stack_get(Runtime* _rt, uint64_t _index, i256* o_ret)
+ EXPORT i256* stack_get(Runtime* _rt, uint64_t _index)
{
auto& stack = _rt->getStack();
- // TODO: encode _index and stack size in the return code
- if (stack.size() <= _index)
- longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall));
-
- *o_ret = *(stack.rbegin() + _index);
+ return _index < stack.size() ? &*(stack.rbegin() + _index) : nullptr;
}
EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word)
{
auto& stack = _rt->getStack();
- // TODO: encode _index and stack size in the return code
- if (stack.size() <= _index)
- longjmp(_rt->getJmpBuf(), static_cast(ReturnCode::StackTooSmall));
+ assert(_index < stack.size());
+ if (_index >= stack.size())
+ return;
*(stack.rbegin() + _index) = *_word;
}
diff --git a/evmjit/libevmjit/Stack.h b/evmjit/libevmjit/Stack.h
index 3e8881e4f..4b6fa374f 100644
--- a/evmjit/libevmjit/Stack.h
+++ b/evmjit/libevmjit/Stack.h
@@ -2,8 +2,6 @@
#include "CompilerHelper.h"
-#include
-
namespace dev
{
namespace eth
@@ -25,11 +23,14 @@ public:
static size_t maxStackSize;
private:
+ llvm::Function* getPopFunc();
+ llvm::Function* getGetFunc();
+
RuntimeManager& m_runtimeManager;
+ llvm::Function* m_pop = nullptr;
llvm::Function* m_push;
- llvm::Function* m_pop;
- llvm::Function* m_get;
+ llvm::Function* m_get = nullptr;
llvm::Function* m_set;
llvm::Value* m_arg;
diff --git a/evmjit/libevmjit/Type.cpp b/evmjit/libevmjit/Type.cpp
index 22ccea12e..8e2bc13fc 100644
--- a/evmjit/libevmjit/Type.cpp
+++ b/evmjit/libevmjit/Type.cpp
@@ -1,8 +1,4 @@
-
#include "Type.h"
-
-#include
-
#include "RuntimeManager.h"
namespace dev
@@ -17,6 +13,8 @@ llvm::PointerType* Type::WordPtr;
llvm::IntegerType* Type::lowPrecision;
llvm::IntegerType* Type::Bool;
llvm::IntegerType* Type::Size;
+llvm::IntegerType* Type::Gas;
+llvm::PointerType* Type::GasPtr;
llvm::IntegerType* Type::Byte;
llvm::PointerType* Type::BytePtr;
llvm::Type* Type::Void;
@@ -24,6 +22,7 @@ llvm::IntegerType* Type::MainReturn;
llvm::PointerType* Type::EnvPtr;
llvm::PointerType* Type::RuntimeDataPtr;
llvm::PointerType* Type::RuntimePtr;
+llvm::ConstantInt* Constant::gasMax;
void Type::init(llvm::LLVMContext& _context)
{
@@ -35,6 +34,8 @@ void Type::init(llvm::LLVMContext& _context)
// TODO: Size should be architecture-dependent
Bool = llvm::Type::getInt1Ty(_context);
Size = llvm::Type::getInt64Ty(_context);
+ Gas = Size;
+ GasPtr = Gas->getPointerTo();
Byte = llvm::Type::getInt8Ty(_context);
BytePtr = Byte->getPointerTo();
Void = llvm::Type::getVoidTy(_context);
@@ -43,6 +44,8 @@ void Type::init(llvm::LLVMContext& _context)
EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo();
RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo();
RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo();
+
+ Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits::max());
}
}
diff --git a/evmjit/libevmjit/Type.h b/evmjit/libevmjit/Type.h
index d4804ee59..b8a4a09eb 100644
--- a/evmjit/libevmjit/Type.h
+++ b/evmjit/libevmjit/Type.h
@@ -1,8 +1,10 @@
-
#pragma once
+#include "preprocessor/llvm_includes_start.h"
#include
-#include
+#include
+#include "preprocessor/llvm_includes_end.h"
+
#include "Common.h"
namespace dev
@@ -23,6 +25,8 @@ struct Type
static llvm::IntegerType* Bool;
static llvm::IntegerType* Size;
+ static llvm::IntegerType* Gas;
+ static llvm::PointerType* GasPtr;
static llvm::IntegerType* Byte;
static llvm::PointerType* BytePtr;
@@ -41,6 +45,8 @@ struct Type
struct Constant
{
+ static llvm::ConstantInt* gasMax;
+
/// Returns word-size constant
static llvm::ConstantInt* get(int64_t _n);
static llvm::ConstantInt* get(llvm::APInt const& _n);
diff --git a/evmjit/libevmjit/Utils.cpp b/evmjit/libevmjit/Utils.cpp
index 5f7f75bfd..bf3bf93b3 100644
--- a/evmjit/libevmjit/Utils.cpp
+++ b/evmjit/libevmjit/Utils.cpp
@@ -1,4 +1,3 @@
-
#include "Utils.h"
namespace dev
diff --git a/evmjit/libevmjit/Utils.h b/evmjit/libevmjit/Utils.h
index 7e6133ced..aad975f5b 100644
--- a/evmjit/libevmjit/Utils.h
+++ b/evmjit/libevmjit/Utils.h
@@ -16,6 +16,9 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } };
//#define clog(CHANNEL) std::cerr
#define clog(CHANNEL) std::ostream(nullptr)
+// The same as assert, but expression is always evaluated and result returned
+#define CHECK(expr) (assert(expr), expr)
+
}
}
}
diff --git a/evmjit/libevmjit/interface.cpp b/evmjit/libevmjit/interface.cpp
index 6b0992dd4..645f3d150 100644
--- a/evmjit/libevmjit/interface.cpp
+++ b/evmjit/libevmjit/interface.cpp
@@ -12,6 +12,7 @@ using namespace dev::eth::jit;
EXPORT void* evmjit_create() noexcept
{
+ // TODO: Make sure ExecutionEngine constructor does not throw
return new(std::nothrow) ExecutionEngine;
}
@@ -22,14 +23,12 @@ EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept
EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept
{
+ if (!_engine || !_data)
+ return static_cast(ReturnCode::UnexpectedException);
+
try
{
- auto codePtr = _data->code;
- auto codeSize = _data->codeSize;
- bytes bytecode;
- bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize);
-
- auto returnCode = _engine->run(bytecode, _data, _env);
+ auto returnCode = _engine->run(_data, _env);
return static_cast(returnCode);
}
catch(...)
diff --git a/evmjit/libevmjit/preprocessor/llvm_includes_end.h b/evmjit/libevmjit/preprocessor/llvm_includes_end.h
new file mode 100644
index 000000000..023c8021e
--- /dev/null
+++ b/evmjit/libevmjit/preprocessor/llvm_includes_end.h
@@ -0,0 +1,5 @@
+#if defined(_MSC_VER)
+ #pragma warning(pop)
+#else
+ #pragma GCC diagnostic pop
+#endif
diff --git a/evmjit/libevmjit/preprocessor/llvm_includes_start.h b/evmjit/libevmjit/preprocessor/llvm_includes_start.h
new file mode 100644
index 000000000..bf34ade99
--- /dev/null
+++ b/evmjit/libevmjit/preprocessor/llvm_includes_start.h
@@ -0,0 +1,8 @@
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable: 4267 4244 4800)
+#else
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wunused-parameter"
+ #pragma GCC diagnostic ignored "-Wconversion"
+#endif
diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp
index 0a94662c8..048134de8 100644
--- a/libdevcrypto/Common.cpp
+++ b/libdevcrypto/Common.cpp
@@ -44,6 +44,8 @@ bool dev::SignatureStruct::isValid() const
return true;
}
+Address dev::ZeroAddress = Address();
+
Public dev::toPublic(Secret const& _secret)
{
Public p;
diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h
index e91df2526..ccbd0953b 100644
--- a/libdevcrypto/Common.h
+++ b/libdevcrypto/Common.h
@@ -62,6 +62,9 @@ struct SignatureStruct
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Address = h160;
+/// The zero address.
+extern Address ZeroAddress;
+
/// A vector of Ethereum addresses.
using Addresses = h160s;
diff --git a/libweb3jsonrpc/AccountHolder.cpp b/libweb3jsonrpc/AccountHolder.cpp
new file mode 100644
index 000000000..b88397953
--- /dev/null
+++ b/libweb3jsonrpc/AccountHolder.cpp
@@ -0,0 +1,108 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file AccountHolder.cpp
+ * @authors:
+ * Christian R
+ * Lefteris Karapetsas
+ * @date 2015
+ */
+
+#include "AccountHolder.h"
+#include
+#include
+#include
+#include
+
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+
+vector g_emptyQueue;
+static std::mt19937 g_randomNumberGenerator(time(0));
+static Mutex x_rngMutex;
+
+void AccountHolder::setAccounts(vector const& _accounts)
+{
+ m_accounts.clear();
+ for (auto const& keyPair: _accounts)
+ {
+ m_accounts.push_back(keyPair.address());
+ m_keyPairs[keyPair.address()] = keyPair;
+ }
+}
+
+vector AccountHolder::getAllAccounts() const
+{
+ vector accounts = m_accounts;
+ for (auto const& pair: m_proxyAccounts)
+ if (!isRealAccount(pair.first))
+ accounts.push_back(pair.first);
+ return accounts;
+}
+
+Address const& AccountHolder::getDefaultTransactAccount() const
+{
+ if (m_accounts.empty())
+ return ZeroAddress;
+ Address const* bestMatch = &m_accounts.front();
+ for (auto const& account: m_accounts)
+ if (m_client()->balanceAt(account) > m_client()->balanceAt(*bestMatch))
+ bestMatch = &account;
+ return *bestMatch;
+}
+
+int AccountHolder::addProxyAccount(const Address& _account)
+{
+ Guard g(x_rngMutex);
+ int id = std::uniform_int_distribution(1)(g_randomNumberGenerator);
+ id = int(u256(FixedHash<32>(sha3(bytesConstRef((byte*)(&id), sizeof(int) / sizeof(byte))))));
+ if (isProxyAccount(_account) || id == 0 || m_transactionQueues.count(id))
+ return 0;
+ m_proxyAccounts.insert(make_pair(_account, id));
+ m_transactionQueues[id].first = _account;
+ return id;
+}
+
+bool AccountHolder::removeProxyAccount(unsigned _id)
+{
+ if (!m_transactionQueues.count(_id))
+ return false;
+ m_proxyAccounts.erase(m_transactionQueues[_id].first);
+ m_transactionQueues.erase(_id);
+ return true;
+}
+
+void AccountHolder::queueTransaction(TransactionSkeleton const& _transaction)
+{
+ if (!m_proxyAccounts.count(_transaction.from))
+ return;
+ int id = m_proxyAccounts[_transaction.from];
+ m_transactionQueues[id].second.push_back(_transaction);
+}
+
+vector const& AccountHolder::getQueuedTransactions(int _id) const
+{
+ if (!m_transactionQueues.count(_id))
+ return g_emptyQueue;
+ return m_transactionQueues.at(_id).second;
+}
+
+void AccountHolder::clearQueue(int _id)
+{
+ if (m_transactionQueues.count(_id))
+ m_transactionQueues.at(_id).second.clear();
+}
diff --git a/libweb3jsonrpc/AccountHolder.h b/libweb3jsonrpc/AccountHolder.h
new file mode 100644
index 000000000..52005b51f
--- /dev/null
+++ b/libweb3jsonrpc/AccountHolder.h
@@ -0,0 +1,74 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file AccountHolder.h
+ * @authors:
+ * Christian R
+ * Lefteris Karapetsas
+ * @date 2015
+ */
+
+#pragma once
+
+#include
+#include
+#include