diff --git a/lib/evmone/CMakeLists.txt b/lib/evmone/CMakeLists.txt index 89c293c560..3126e2d8ae 100644 --- a/lib/evmone/CMakeLists.txt +++ b/lib/evmone/CMakeLists.txt @@ -11,6 +11,8 @@ add_library(evmone ${include_dir}/evmone/evmone.h analysis.cpp analysis.hpp + baseline.cpp + baseline.hpp evmone.cpp execution.cpp execution.hpp @@ -21,7 +23,7 @@ add_library(evmone limits.hpp opcodes_helpers.h ) -target_link_libraries(evmone PUBLIC evmc::evmc PRIVATE intx::intx ethash::keccak) +target_link_libraries(evmone PUBLIC evmc::evmc PRIVATE intx::intx evmc::instructions ethash::keccak) target_include_directories(evmone PUBLIC $$ ) diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp new file mode 100644 index 0000000000..ba1a10fb5d --- /dev/null +++ b/lib/evmone/baseline.cpp @@ -0,0 +1,727 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2020 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "baseline.hpp" +#include "execution_state.hpp" +#include "instructions.hpp" +#include +#include + +namespace evmone +{ +namespace +{ +using JumpdestMap = std::vector; + +JumpdestMap build_jumpdest_map(const uint8_t* code, size_t code_size) +{ + JumpdestMap m(code_size); + for (size_t i = 0; i < code_size; ++i) + { + const auto op = code[i]; + if (op == OP_JUMPDEST) + m[i] = true; + else if (op >= OP_PUSH1 && op <= OP_PUSH32) + i += static_cast(op - OP_PUSH1 + 1); + } + return m; +} + +const uint8_t* op_jump(ExecutionState& state, const JumpdestMap& jumpdest_map) noexcept +{ + const auto dst = state.stack.pop(); + if (dst >= jumpdest_map.size() || !jumpdest_map[static_cast(dst)]) + { + state.status = EVMC_BAD_JUMP_DESTINATION; + return &state.code[0] + state.code.size(); + } + + return &state.code[static_cast(dst)]; +} + +template +inline const uint8_t* load_push( + ExecutionState& state, const uint8_t* code, const uint8_t* code_end) noexcept +{ + // TODO: Also last full push can be ignored. + if (code + Len > code_end) // Trimmed push data can be ignored. + return code_end; + + uint8_t buffer[Len]; + std::memcpy(buffer, code, Len); + state.stack.push(intx::be::load(buffer)); + return code + Len; +} + +template +inline void op_return(ExecutionState& state) noexcept +{ + const auto offset = state.stack[0]; + const auto size = state.stack[1]; + + if (!check_memory(state, offset, size)) + { + state.status = EVMC_OUT_OF_GAS; + return; + } + + state.output_offset = static_cast(offset); // Can be garbage if size is 0. + state.output_size = static_cast(size); + state.status = StatusCode; +} + +inline evmc_status_code check_requirements(const char* const* instruction_names, + const evmc_instruction_metrics* instruction_metrics, ExecutionState& state, uint8_t op) noexcept +{ + const auto metrics = instruction_metrics[op]; + + if (instruction_names[op] == nullptr) + return EVMC_UNDEFINED_INSTRUCTION; + + if ((state.gas_left -= metrics.gas_cost) < 0) + return EVMC_OUT_OF_GAS; + + const auto stack_size = state.stack.size(); + if (stack_size < metrics.stack_height_required) + return EVMC_STACK_UNDERFLOW; + if (stack_size + metrics.stack_height_change > evm_stack::limit) + return EVMC_STACK_OVERFLOW; + + return EVMC_SUCCESS; +} +} // namespace + +evmc_result baseline_execute(evmc_vm* /*vm*/, const evmc_host_interface* host, + evmc_host_context* ctx, evmc_revision rev, const evmc_message* msg, const uint8_t* code, + size_t code_size) noexcept +{ + const auto instruction_names = evmc_get_instruction_names_table(rev); + const auto instruction_metrics = evmc_get_instruction_metrics_table(rev); + const auto jumpdest_map = build_jumpdest_map(code, code_size); + + auto state = std::make_unique(*msg, rev, *host, ctx, code, code_size); + + const auto code_end = code + code_size; + auto* pc = code; + while (pc != code_end) + { + const auto op = *pc; + + const auto status = check_requirements(instruction_names, instruction_metrics, *state, op); + if (status != EVMC_SUCCESS) + { + state->status = status; + goto exit; + } + + switch (op) + { + case OP_STOP: + goto exit; + case OP_ADD: + add(state->stack); + break; + case OP_MUL: + mul(state->stack); + break; + case OP_SUB: + sub(state->stack); + break; + case OP_DIV: + div(state->stack); + break; + case OP_SDIV: + sdiv(state->stack); + break; + case OP_MOD: + mod(state->stack); + break; + case OP_SMOD: + smod(state->stack); + break; + case OP_ADDMOD: + addmod(state->stack); + break; + case OP_MULMOD: + mulmod(state->stack); + break; + case OP_EXP: + { + const auto status_code = exp(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_SIGNEXTEND: + signextend(state->stack); + break; + + case OP_LT: + lt(state->stack); + break; + case OP_GT: + gt(state->stack); + break; + case OP_SLT: + slt(state->stack); + break; + case OP_SGT: + sgt(state->stack); + break; + case OP_EQ: + eq(state->stack); + break; + case OP_ISZERO: + iszero(state->stack); + break; + case OP_AND: + and_(state->stack); + break; + case OP_OR: + or_(state->stack); + break; + case OP_XOR: + xor_(state->stack); + break; + case OP_NOT: + not_(state->stack); + break; + case OP_BYTE: + byte(state->stack); + break; + case OP_SHL: + shl(state->stack); + break; + case OP_SHR: + shr(state->stack); + break; + case OP_SAR: + sar(state->stack); + break; + + case OP_SHA3: + { + const auto status_code = sha3(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + + case OP_ADDRESS: + address(*state); + break; + case OP_BALANCE: + balance(*state); + break; + case OP_ORIGIN: + origin(*state); + break; + case OP_CALLER: + caller(*state); + break; + case OP_CALLVALUE: + callvalue(*state); + break; + case OP_CALLDATALOAD: + calldataload(*state); + break; + case OP_CALLDATASIZE: + calldatasize(*state); + break; + case OP_CALLDATACOPY: + { + const auto status_code = calldatacopy(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_CODESIZE: + state->stack.push(code_size); + break; + case OP_CODECOPY: + { + const auto status_code = codecopy(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_GASPRICE: + gasprice(*state); + break; + case OP_EXTCODESIZE: + extcodesize(*state); + break; + case OP_EXTCODECOPY: + { + const auto status_code = extcodecopy(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_RETURNDATASIZE: + returndatasize(*state); + break; + case OP_RETURNDATACOPY: + { + const auto status_code = returndatacopy(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_EXTCODEHASH: + extcodehash(*state); + break; + + case OP_BLOCKHASH: + blockhash(*state); + break; + case OP_COINBASE: + coinbase(*state); + break; + case OP_TIMESTAMP: + timestamp(*state); + break; + case OP_NUMBER: + number(*state); + break; + case OP_DIFFICULTY: + difficulty(*state); + break; + case OP_GASLIMIT: + gaslimit(*state); + break; + case OP_CHAINID: + chainid(*state); + break; + case OP_SELFBALANCE: + selfbalance(*state); + break; + + case OP_POP: + pop(state->stack); + break; + case OP_MLOAD: + { + const auto status_code = mload(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_MSTORE: + { + const auto status_code = mstore(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_MSTORE8: + { + const auto status_code = mstore8(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + + case OP_JUMP: + pc = op_jump(*state, jumpdest_map); + continue; + case OP_JUMPI: + if (state->stack[1] != 0) + { + pc = op_jump(*state, jumpdest_map); + } + else + { + state->stack.pop(); + ++pc; + } + state->stack.pop(); + continue; + + case OP_PC: + state->stack.push(pc - code); + break; + case OP_MSIZE: + msize(*state); + break; + case OP_SLOAD: + sload(*state); + break; + case OP_SSTORE: + { + const auto status_code = sstore(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_GAS: + state->stack.push(state->gas_left); + break; + case OP_JUMPDEST: + break; + + case OP_PUSH1: + pc = load_push<1>(*state, pc + 1, code_end); + continue; + case OP_PUSH2: + pc = load_push<2>(*state, pc + 1, code_end); + continue; + case OP_PUSH3: + pc = load_push<3>(*state, pc + 1, code_end); + continue; + case OP_PUSH4: + pc = load_push<4>(*state, pc + 1, code_end); + continue; + case OP_PUSH5: + pc = load_push<5>(*state, pc + 1, code_end); + continue; + case OP_PUSH6: + pc = load_push<6>(*state, pc + 1, code_end); + continue; + case OP_PUSH7: + pc = load_push<7>(*state, pc + 1, code_end); + continue; + case OP_PUSH8: + pc = load_push<8>(*state, pc + 1, code_end); + continue; + case OP_PUSH9: + pc = load_push<9>(*state, pc + 1, code_end); + continue; + case OP_PUSH10: + pc = load_push<10>(*state, pc + 1, code_end); + continue; + case OP_PUSH11: + pc = load_push<11>(*state, pc + 1, code_end); + continue; + case OP_PUSH12: + pc = load_push<12>(*state, pc + 1, code_end); + continue; + case OP_PUSH13: + pc = load_push<13>(*state, pc + 1, code_end); + continue; + case OP_PUSH14: + pc = load_push<14>(*state, pc + 1, code_end); + continue; + case OP_PUSH15: + pc = load_push<15>(*state, pc + 1, code_end); + continue; + case OP_PUSH16: + pc = load_push<16>(*state, pc + 1, code_end); + continue; + case OP_PUSH17: + pc = load_push<17>(*state, pc + 1, code_end); + continue; + case OP_PUSH18: + pc = load_push<18>(*state, pc + 1, code_end); + continue; + case OP_PUSH19: + pc = load_push<19>(*state, pc + 1, code_end); + continue; + case OP_PUSH20: + pc = load_push<20>(*state, pc + 1, code_end); + continue; + case OP_PUSH21: + pc = load_push<21>(*state, pc + 1, code_end); + continue; + case OP_PUSH22: + pc = load_push<22>(*state, pc + 1, code_end); + continue; + case OP_PUSH23: + pc = load_push<23>(*state, pc + 1, code_end); + continue; + case OP_PUSH24: + pc = load_push<24>(*state, pc + 1, code_end); + continue; + case OP_PUSH25: + pc = load_push<25>(*state, pc + 1, code_end); + continue; + case OP_PUSH26: + pc = load_push<26>(*state, pc + 1, code_end); + continue; + case OP_PUSH27: + pc = load_push<27>(*state, pc + 1, code_end); + continue; + case OP_PUSH28: + pc = load_push<28>(*state, pc + 1, code_end); + continue; + case OP_PUSH29: + pc = load_push<29>(*state, pc + 1, code_end); + continue; + case OP_PUSH30: + pc = load_push<30>(*state, pc + 1, code_end); + continue; + case OP_PUSH31: + pc = load_push<31>(*state, pc + 1, code_end); + continue; + case OP_PUSH32: + pc = load_push<32>(*state, pc + 1, code_end); + continue; + + case OP_DUP1: + dup<1>(state->stack); + break; + case OP_DUP2: + dup<2>(state->stack); + break; + case OP_DUP3: + dup<3>(state->stack); + break; + case OP_DUP4: + dup<4>(state->stack); + break; + case OP_DUP5: + dup<5>(state->stack); + break; + case OP_DUP6: + dup<6>(state->stack); + break; + case OP_DUP7: + dup<7>(state->stack); + break; + case OP_DUP8: + dup<8>(state->stack); + break; + case OP_DUP9: + dup<9>(state->stack); + break; + case OP_DUP10: + dup<10>(state->stack); + break; + case OP_DUP11: + dup<11>(state->stack); + break; + case OP_DUP12: + dup<12>(state->stack); + break; + case OP_DUP13: + dup<13>(state->stack); + break; + case OP_DUP14: + dup<14>(state->stack); + break; + case OP_DUP15: + dup<15>(state->stack); + break; + case OP_DUP16: + dup<16>(state->stack); + break; + + case OP_SWAP1: + swap<1>(state->stack); + break; + case OP_SWAP2: + swap<2>(state->stack); + break; + case OP_SWAP3: + swap<3>(state->stack); + break; + case OP_SWAP4: + swap<4>(state->stack); + break; + case OP_SWAP5: + swap<5>(state->stack); + break; + case OP_SWAP6: + swap<6>(state->stack); + break; + case OP_SWAP7: + swap<7>(state->stack); + break; + case OP_SWAP8: + swap<8>(state->stack); + break; + case OP_SWAP9: + swap<9>(state->stack); + break; + case OP_SWAP10: + swap<10>(state->stack); + break; + case OP_SWAP11: + swap<11>(state->stack); + break; + case OP_SWAP12: + swap<12>(state->stack); + break; + case OP_SWAP13: + swap<13>(state->stack); + break; + case OP_SWAP14: + swap<14>(state->stack); + break; + case OP_SWAP15: + swap<15>(state->stack); + break; + case OP_SWAP16: + swap<16>(state->stack); + break; + + case OP_LOG0: + { + const auto status_code = log(*state, 0); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_LOG1: + { + const auto status_code = log(*state, 1); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_LOG2: + { + const auto status_code = log(*state, 2); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_LOG3: + { + const auto status_code = log(*state, 3); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_LOG4: + { + const auto status_code = log(*state, 4); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + + case OP_CREATE: + { + const auto status_code = create(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_CALL: + { + const auto status_code = call(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_CALLCODE: + { + const auto status_code = call(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_RETURN: + op_return(*state); + goto exit; + case OP_DELEGATECALL: + { + const auto status_code = call(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_STATICCALL: + { + const auto status_code = call(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_CREATE2: + { + const auto status_code = create(*state); + if (status_code != EVMC_SUCCESS) + { + state->status = status_code; + goto exit; + } + break; + } + case OP_REVERT: + op_return(*state); + goto exit; + case OP_INVALID: + state->status = EVMC_INVALID_INSTRUCTION; + goto exit; + case OP_SELFDESTRUCT: + state->status = selfdestruct(*state); + goto exit; + default: + INTX_UNREACHABLE(); + } + + ++pc; + } + +exit: + const auto gas_left = + (state->status == EVMC_SUCCESS || state->status == EVMC_REVERT) ? state->gas_left : 0; + + return evmc::make_result(state->status, gas_left, + state->output_size != 0 ? &state->memory[state->output_offset] : nullptr, + state->output_size); +} +} // namespace evmone diff --git a/lib/evmone/baseline.hpp b/lib/evmone/baseline.hpp new file mode 100644 index 0000000000..6223b0e57f --- /dev/null +++ b/lib/evmone/baseline.hpp @@ -0,0 +1,12 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2020 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +namespace evmone +{ +evmc_result baseline_execute(evmc_vm* vm, const evmc_host_interface* host, evmc_host_context* ctx, + evmc_revision rev, const evmc_message* msg, const uint8_t* code, size_t code_size) noexcept; +} // namespace evmone diff --git a/lib/evmone/evmone.cpp b/lib/evmone/evmone.cpp index 6b2559b0b1..fe87f86a25 100644 --- a/lib/evmone/evmone.cpp +++ b/lib/evmone/evmone.cpp @@ -6,6 +6,7 @@ /// EVMC instance and entry point of evmone is defined here. /// The file name matches the evmone.h public header. +#include "baseline.hpp" #include "execution.hpp" #include @@ -24,12 +25,18 @@ constexpr evmc_capabilities_flagset get_capabilities(evmc_vm* /*vm*/) noexcept return EVMC_CAPABILITY_EVM1; } -evmc_set_option_result set_option(evmc_vm* /*vm*/, char const* name, char const* value) noexcept +evmc_set_option_result set_option(evmc_vm* vm, char const* name, char const* value) noexcept { if (name[0] == 'O' && name[1] == '\0') { - if (value[0] == '2' && value[1] == '\0') // O=2 + if (value[0] == '0' && value[1] == '\0') // O=0 { + vm->execute = evmone::baseline_execute; + return EVMC_SET_OPTION_SUCCESS; + } + else if (value[0] == '2' && value[1] == '\0') // O=2 + { + vm->execute = evmone::execute; return EVMC_SET_OPTION_SUCCESS; } return EVMC_SET_OPTION_INVALID_VALUE; diff --git a/test/bench/bench.cpp b/test/bench/bench.cpp index fcc31dba76..1116c72aaa 100644 --- a/test/bench/bench.cpp +++ b/test/bench/bench.cpp @@ -336,6 +336,7 @@ int main(int argc, char** argv) return ec; registered_vms["advanced"] = evmc::VM{evmc_create_evmone(), {{"O", "2"}}}; + registered_vms["baseline"] = evmc::VM{evmc_create_evmone(), {{"O", "0"}}}; register_benchmarks(benchmark_cases); RunSpecifiedBenchmarks(); return 0; diff --git a/test/unittests/evm_fixture.cpp b/test/unittests/evm_fixture.cpp index a0ffdccaf7..c166583460 100644 --- a/test/unittests/evm_fixture.cpp +++ b/test/unittests/evm_fixture.cpp @@ -10,14 +10,17 @@ namespace evmone::test namespace { evmc::VM advanced_vm{evmc_create_evmone(), {{"O", "2"}}}; +evmc::VM baseline_vm{evmc_create_evmone(), {{"O", "0"}}}; const char* print_vm_name(const testing::TestParamInfo& info) noexcept { if (info.param == &advanced_vm) return "advanced"; - return ""; + if (info.param == &baseline_vm) + return "baseline"; + return "unknown"; } } // namespace -INSTANTIATE_TEST_SUITE_P(evmone, evm, testing::Values(&advanced_vm), print_vm_name); +INSTANTIATE_TEST_SUITE_P(evmone, evm, testing::Values(&advanced_vm, &baseline_vm), print_vm_name); } // namespace evmone::test diff --git a/test/unittests/evmone_test.cpp b/test/unittests/evmone_test.cpp index 86da4f6231..e29ba48ad1 100644 --- a/test/unittests/evmone_test.cpp +++ b/test/unittests/evmone_test.cpp @@ -35,7 +35,7 @@ TEST(evmone, set_option_optimization_level) { auto vm = evmc::VM{evmc_create_evmone()}; EXPECT_EQ(vm.set_option("O", ""), EVMC_SET_OPTION_INVALID_VALUE); - EXPECT_EQ(vm.set_option("O", "0"), EVMC_SET_OPTION_INVALID_VALUE); + EXPECT_EQ(vm.set_option("O", "0"), EVMC_SET_OPTION_SUCCESS); EXPECT_EQ(vm.set_option("O", "1"), EVMC_SET_OPTION_INVALID_VALUE); EXPECT_EQ(vm.set_option("O", "2"), EVMC_SET_OPTION_SUCCESS); EXPECT_EQ(vm.set_option("O", "3"), EVMC_SET_OPTION_INVALID_VALUE);