diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..26c29d38d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "evmjit"] + path = evmjit + url = https://github.com/ethereum/evmjit + branch = develop diff --git a/CMakeLists.txt b/CMakeLists.txt index 5763c9b85..df33ca0e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ function(createDefaultCacheConfig) set(VMTRACE OFF CACHE BOOL "VM tracing and run-time checks (useful for cross-implementation VM debugging)") set(PARANOIA OFF CACHE BOOL "Additional run-time checks") set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on") + set(EVMJIT OFF CACHE BOOL "Build a just-in-time compiler for EVM code (requires LLVM)") endfunction() @@ -38,6 +39,10 @@ function(configureProject) message(FATAL_ERROR "VM tracing requires debug.") endif () endif () + + if (EVMJIT) + add_definitions(-DETH_EVMJIT) + endif() endfunction() @@ -84,7 +89,7 @@ cmake_policy(SET CMP0015 NEW) createDefaultCacheConfig() configureProject() -message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}") +message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}") # Default TARGET_PLATFORM to "linux". @@ -151,6 +156,10 @@ if (NOT HEADLESS) endif() +if (EVMJIT) + add_subdirectory(evmjit) +endif() + enable_testing() add_test(NAME alltests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND testeth) diff --git a/evmjit b/evmjit new file mode 160000 index 000000000..ed63ced24 --- /dev/null +++ b/evmjit @@ -0,0 +1 @@ +Subproject commit ed63ced24456c28b6dba345b8fc61e680ed406d7 diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 8fc4fedb3..ec8ca605c 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -65,6 +65,8 @@ bytes dev::contents(std::string const& _file) // get length of file: is.seekg (0, is.end); streamoff length = is.tellg(); + if (length == 0) // return early, MSVC does not like reading 0 bytes + return {}; is.seekg (0, is.beg); bytes ret(length); is.read((char*)ret.data(), length); diff --git a/libevm/CMakeLists.txt b/libevm/CMakeLists.txt index 96b8f9ade..4af5eb175 100644 --- a/libevm/CMakeLists.txt +++ b/libevm/CMakeLists.txt @@ -30,6 +30,9 @@ target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcore) +if (EVMJIT) + target_link_libraries(${EXECUTABLE} evmjit-cpp) +endif() install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) diff --git a/libevm/VMFactory.cpp b/libevm/VMFactory.cpp index af37ec710..b6549ba04 100644 --- a/libevm/VMFactory.cpp +++ b/libevm/VMFactory.cpp @@ -18,6 +18,10 @@ #include "VMFactory.h" #include "VM.h" +#if ETH_EVMJIT +#include +#endif + namespace dev { namespace eth @@ -34,8 +38,12 @@ void VMFactory::setKind(VMKind _kind) std::unique_ptr VMFactory::create(u256 _gas) { - asserts(g_kind == VMKind::Interpreter && "Only interpreter supported for now"); +#if ETH_EVMJIT + return std::unique_ptr(g_kind == VMKind::JIT ? (VMFace*)new JitVM(_gas) : new VM(_gas)); +#else + asserts(g_kind == VMKind::Interpreter && "JIT disabled in build configuration"); return std::unique_ptr(new VM(_gas)); +#endif } } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index b700b65a7..ea848c7ce 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -26,6 +26,7 @@ #include #include #include +#include using namespace std; using namespace dev::eth; @@ -330,7 +331,7 @@ void checkStorage(map _expectedStore, map _resultStore, } } BOOST_CHECK_EQUAL(_resultStore.size(), _expectedStore.size()); - for (auto&& resultStorePair : _resultStore) + for (auto&& resultStorePair: _resultStore) { if (!_expectedStore.count(resultStorePair.first)) BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first); @@ -472,4 +473,20 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun } } + +void processCommandLineOptions() +{ + auto argc = boost::unit_test::framework::master_test_suite().argc; + auto argv = boost::unit_test::framework::master_test_suite().argv; + + for (auto i = 0; i < argc; ++i) + { + if (std::string(argv[i]) == "--jit") + { + eth::VMFactory::setKind(eth::VMKind::JIT); + break; + } + } +} + } } // namespaces diff --git a/test/TestHelper.h b/test/TestHelper.h index 3203eae83..20328c913 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -76,6 +76,7 @@ void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs); void executeTests(const std::string& _name, const std::string& _testPathAppendix, std::function doTests); std::string getTestPath(); void userDefinedTest(std::string testTypeFlag, std::function doTests); +void processCommandLineOptions(); template void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) diff --git a/test/createRandomTest.cpp b/test/createRandomTest.cpp index a11688457..1af12f64d 100644 --- a/test/createRandomTest.cpp +++ b/test/createRandomTest.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** @file createRandomTest.cpp * @author Christoph Jentzsch diff --git a/test/state.cpp b/test/state.cpp index d8a3dd283..b1ad8d44e 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -43,6 +43,8 @@ namespace dev { namespace test { void doStateTests(json_spirit::mValue& v, bool _fillin) { + processCommandLineOptions(); + for (auto& i: v.get_obj()) { cerr << i.first << endl; diff --git a/test/vm.cpp b/test/vm.cpp index 010eb4d75..920f0582c 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -20,6 +20,7 @@ * vm test functions. */ +#include #include #include #include @@ -308,6 +309,8 @@ namespace dev { namespace test { void doVMTests(json_spirit::mValue& v, bool _fillin) { + processCommandLineOptions(); + for (auto& i: v.get_obj()) { cnote << i.first; @@ -317,7 +320,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) BOOST_REQUIRE(o.count("pre") > 0); BOOST_REQUIRE(o.count("exec") > 0); - dev::test::FakeExtVM fev; + FakeExtVM fev; fev.importEnv(o["env"].get_obj()); fev.importState(o["pre"].get_obj()); @@ -332,12 +335,12 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) } bytes output; - auto vm = eth::VMFactory::create(fev.gas); - u256 gas; bool vmExceptionOccured = false; + auto startTime = std::chrono::high_resolution_clock::now(); try { + auto vm = eth::VMFactory::create(fev.gas); output = vm->go(fev, fev.simpleTrace()).toBytes(); gas = vm->gas(); } @@ -357,6 +360,21 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) BOOST_ERROR("Failed VM Test with Exception: " << _e.what()); } + auto endTime = std::chrono::high_resolution_clock::now(); + auto argc = boost::unit_test::framework::master_test_suite().argc; + auto argv = boost::unit_test::framework::master_test_suite().argv; + for (auto i = 0; i < argc; ++i) + { + if (std::string(argv[i]) == "--show-times") + { + auto testDuration = endTime - startTime; + cnote << "Execution time: " + << std::chrono::duration_cast(testDuration).count() + << " ms"; + break; + } + } + // delete null entries in storage for the sake of comparison for (auto &a: fev.addresses)