diff --git a/lib/evmone/CMakeLists.txt b/lib/evmone/CMakeLists.txt index e57eb523fe..c4184340b6 100644 --- a/lib/evmone/CMakeLists.txt +++ b/lib/evmone/CMakeLists.txt @@ -18,7 +18,7 @@ add_library(evmone limits.hpp opcodes_helpers.h ) -target_link_libraries(evmone PUBLIC evmc::evmc PRIVATE intx::intx evmc::instructions ethash::keccak) +target_link_libraries(evmone PUBLIC evmc::evmc PRIVATE intx::intx ethash::keccak) target_include_directories(evmone PUBLIC $$ ) diff --git a/lib/evmone/analysis.cpp b/lib/evmone/analysis.cpp index 123b729ae8..9e4ef770c4 100644 --- a/lib/evmone/analysis.cpp +++ b/lib/evmone/analysis.cpp @@ -32,8 +32,8 @@ inline constexpr uint64_t load64be(const unsigned char* data) noexcept code_analysis analyze(evmc_revision rev, const uint8_t* code, size_t code_size) noexcept { - const auto& fns = get_op_table(rev); - const auto opx_beginblock_fn = fns[OPX_BEGINBLOCK]; + const auto& op_tbl = get_op_table(rev); + const auto opx_beginblock_fn = op_tbl[OPX_BEGINBLOCK].fn; code_analysis analysis; @@ -44,8 +44,6 @@ code_analysis analyze(evmc_revision rev, const uint8_t* code, size_t code_size) const auto max_args_storage_size = code_size + 1; analysis.push_values.reserve(max_args_storage_size); - const auto* instr_table = evmc_get_instruction_metrics_table(rev); - // Create first block. analysis.instrs.emplace_back(opx_beginblock_fn); auto block = block_analysis{0}; @@ -56,17 +54,13 @@ code_analysis analyze(evmc_revision rev, const uint8_t* code, size_t code_size) while (code_pos != code_end) { const auto opcode = *code_pos++; + const auto& opcode_info = op_tbl[opcode]; - const auto metrics = instr_table[opcode]; - const auto instr_stack_req = metrics.num_stack_arguments; - const auto instr_stack_change = metrics.num_stack_returned_items - instr_stack_req; - - block.stack_req = std::max(block.stack_req, instr_stack_req - block.stack_change); - block.stack_change += instr_stack_change; + block.stack_req = std::max(block.stack_req, opcode_info.stack_req - block.stack_change); + block.stack_change += opcode_info.stack_change; block.stack_max_growth = std::max(block.stack_max_growth, block.stack_change); - if (metrics.gas_cost > 0) // can be -1 for undefined instruction - block.gas_cost += metrics.gas_cost; + block.gas_cost += opcode_info.gas_cost; if (opcode == OP_JUMPDEST) { @@ -77,7 +71,7 @@ code_analysis analyze(evmc_revision rev, const uint8_t* code, size_t code_size) static_cast(analysis.instrs.size() - 1)); } else - analysis.instrs.emplace_back(fns[opcode]); + analysis.instrs.emplace_back(opcode_info.fn); auto& instr = analysis.instrs.back(); @@ -177,7 +171,7 @@ code_analysis analyze(evmc_revision rev, const uint8_t* code, size_t code_size) // Make sure the last block is terminated. // TODO: This is not needed if the last instruction is a terminating one. - analysis.instrs.emplace_back(fns[OP_STOP]); + analysis.instrs.emplace_back(op_tbl[OP_STOP].fn); // FIXME: assert(analysis.instrs.size() <= max_instrs_size); diff --git a/lib/evmone/analysis.hpp b/lib/evmone/analysis.hpp index 0c8900981a..04a0c1101f 100644 --- a/lib/evmone/analysis.hpp +++ b/lib/evmone/analysis.hpp @@ -170,7 +170,15 @@ enum intrinsic_opcodes OPX_BEGINBLOCK = OP_JUMPDEST }; -using exec_fn_table = std::array; +struct op_table_entry +{ + exec_fn fn; + int16_t gas_cost; + int8_t stack_req; + int8_t stack_change; +}; + +using op_table = std::array; struct instr_info { @@ -213,6 +221,6 @@ inline int find_jumpdest(const code_analysis& analysis, int offset) noexcept EVMC_EXPORT code_analysis analyze( evmc_revision rev, const uint8_t* code, size_t code_size) noexcept; -EVMC_EXPORT const exec_fn_table& get_op_table(evmc_revision rev) noexcept; +EVMC_EXPORT const op_table& get_op_table(evmc_revision rev) noexcept; } // namespace evmone diff --git a/lib/evmone/instructions.cpp b/lib/evmone/instructions.cpp index c8118cb18a..2543b78bbb 100644 --- a/lib/evmone/instructions.cpp +++ b/lib/evmone/instructions.cpp @@ -1217,172 +1217,187 @@ const instr_info* opx_beginblock(const instr_info* instr, execution_state& state return ++instr; } -constexpr exec_fn_table create_op_table_frontier() noexcept +constexpr op_table create_op_table_frontier() noexcept { - auto table = exec_fn_table{}; + auto table = op_table{}; // First, mark all opcodes as undefined. for (auto& t : table) - t = op_undefined; - - table[OP_STOP] = op_stop; - table[OP_ADD] = op_add; - table[OP_MUL] = op_mul; - table[OP_SUB] = op_sub; - table[OP_DIV] = op_div; - table[OP_SDIV] = op_sdiv; - table[OP_MOD] = op_mod; - table[OP_SMOD] = op_smod; - table[OP_ADDMOD] = op_addmod; - table[OP_MULMOD] = op_mulmod; - table[OP_EXP] = op_exp; - table[OP_SIGNEXTEND] = op_signextend; - table[OP_LT] = op_lt; - table[OP_GT] = op_gt; - table[OP_SLT] = op_slt; - table[OP_SGT] = op_sgt; - table[OP_EQ] = op_eq; - table[OP_ISZERO] = op_iszero; - table[OP_AND] = op_and; - table[OP_OR] = op_or; - table[OP_XOR] = op_xor; - table[OP_NOT] = op_not; - table[OP_BYTE] = op_byte; - table[OP_SHA3] = op_sha3; - table[OP_ADDRESS] = op_address; - table[OP_BALANCE] = op_balance; - table[OP_ORIGIN] = op_origin; - table[OP_CALLER] = op_caller; - table[OP_CALLVALUE] = op_callvalue; - table[OP_CALLDATALOAD] = op_calldataload; - table[OP_CALLDATASIZE] = op_calldatasize; - table[OP_CALLDATACOPY] = op_calldatacopy; - table[OP_CODESIZE] = op_codesize; - table[OP_CODECOPY] = op_codecopy; - table[OP_EXTCODESIZE] = op_extcodesize; - table[OP_EXTCODECOPY] = op_extcodecopy; - table[OP_GASPRICE] = op_gasprice; - table[OP_BLOCKHASH] = op_blockhash; - table[OP_COINBASE] = op_coinbase; - table[OP_TIMESTAMP] = op_timestamp; - table[OP_NUMBER] = op_number; - table[OP_DIFFICULTY] = op_difficulty; - table[OP_GASLIMIT] = op_gaslimit; - table[OP_POP] = op_pop; - table[OP_MLOAD] = op_mload; - table[OP_MSTORE] = op_mstore; - table[OP_MSTORE8] = op_mstore8; - table[OP_SLOAD] = op_sload; - table[OP_SSTORE] = op_sstore; - table[OP_JUMP] = op_jump; - table[OP_JUMPI] = op_jumpi; - table[OP_PC] = op_pc; - table[OP_MSIZE] = op_msize; - table[OP_GAS] = op_gas; - table[OPX_BEGINBLOCK] = opx_beginblock; // Replaces JUMPDEST. + t = {op_undefined, 0, 0, 0}; + + table[OP_STOP] = {op_stop, 0, 0, 0}; + table[OP_ADD] = {op_add, 3, 2, -1}; + table[OP_MUL] = {op_mul, 5, 2, -1}; + table[OP_SUB] = {op_sub, 3, 2, -1}; + table[OP_DIV] = {op_div, 5, 2, -1}; + table[OP_SDIV] = {op_sdiv, 5, 2, -1}; + table[OP_MOD] = {op_mod, 5, 2, -1}; + table[OP_SMOD] = {op_smod, 5, 2, -1}; + table[OP_ADDMOD] = {op_addmod, 8, 3, -2}; + table[OP_MULMOD] = {op_mulmod, 8, 3, -2}; + table[OP_EXP] = {op_exp, 10, 2, -1}; + table[OP_SIGNEXTEND] = {op_signextend, 5, 2, -1}; + table[OP_LT] = {op_lt, 3, 2, -1}; + table[OP_GT] = {op_gt, 3, 2, -1}; + table[OP_SLT] = {op_slt, 3, 2, -1}; + table[OP_SGT] = {op_sgt, 3, 2, -1}; + table[OP_EQ] = {op_eq, 3, 2, -1}; + table[OP_ISZERO] = {op_iszero, 3, 1, 0}; + table[OP_AND] = {op_and, 3, 2, -1}; + table[OP_OR] = {op_or, 3, 2, -1}; + table[OP_XOR] = {op_xor, 3, 2, -1}; + table[OP_NOT] = {op_not, 3, 1, 0}; + table[OP_BYTE] = {op_byte, 3, 2, -1}; + table[OP_SHA3] = {op_sha3, 30, 2, -1}; + table[OP_ADDRESS] = {op_address, 2, 0, 1}; + table[OP_BALANCE] = {op_balance, 20, 1, 0}; + table[OP_ORIGIN] = {op_origin, 2, 0, 1}; + table[OP_CALLER] = {op_caller, 2, 0, 1}; + table[OP_CALLVALUE] = {op_callvalue, 2, 0, 1}; + table[OP_CALLDATALOAD] = {op_calldataload, 3, 1, 0}; + table[OP_CALLDATASIZE] = {op_calldatasize, 2, 0, 1}; + table[OP_CALLDATACOPY] = {op_calldatacopy, 3, 3, -3}; + table[OP_CODESIZE] = {op_codesize, 2, 0, 1}; + table[OP_CODECOPY] = {op_codecopy, 3, 3, -3}; + table[OP_GASPRICE] = {op_gasprice, 2, 0, 1}; + table[OP_EXTCODESIZE] = {op_extcodesize, 20, 1, 0}; + table[OP_EXTCODECOPY] = {op_extcodecopy, 20, 4, -4}; + table[OP_BLOCKHASH] = {op_blockhash, 20, 1, 0}; + table[OP_COINBASE] = {op_coinbase, 2, 0, 1}; + table[OP_TIMESTAMP] = {op_timestamp, 2, 0, 1}; + table[OP_NUMBER] = {op_number, 2, 0, 1}; + table[OP_DIFFICULTY] = {op_difficulty, 2, 0, 1}; + table[OP_GASLIMIT] = {op_gaslimit, 2, 0, 1}; + table[OP_POP] = {op_pop, 2, 1, -1}; + table[OP_MLOAD] = {op_mload, 3, 1, 0}; + table[OP_MSTORE] = {op_mstore, 3, 2, -2}; + table[OP_MSTORE8] = {op_mstore8, 3, 2, -2}; + table[OP_SLOAD] = {op_sload, 50, 1, 0}; + table[OP_SSTORE] = {op_sstore, 0, 2, -2}; + table[OP_JUMP] = {op_jump, 8, 1, -1}; + table[OP_JUMPI] = {op_jumpi, 10, 2, -2}; + table[OP_PC] = {op_pc, 2, 0, 1}; + table[OP_MSIZE] = {op_msize, 2, 0, 1}; + table[OP_GAS] = {op_gas, 2, 0, 1}; + table[OPX_BEGINBLOCK] = {opx_beginblock, 1, 0, 0}; // Replaces JUMPDEST. + for (auto op = size_t{OP_PUSH1}; op <= OP_PUSH8; ++op) - table[op] = op_push_small; + table[op] = {op_push_small, 3, 0, 1}; for (auto op = size_t{OP_PUSH9}; op <= OP_PUSH32; ++op) - table[op] = op_push_full; - - table[OP_DUP1] = op_dup; - table[OP_DUP2] = op_dup; - table[OP_DUP3] = op_dup; - table[OP_DUP4] = op_dup; - table[OP_DUP5] = op_dup; - table[OP_DUP6] = op_dup; - table[OP_DUP7] = op_dup; - table[OP_DUP8] = op_dup; - table[OP_DUP9] = op_dup; - table[OP_DUP10] = op_dup; - table[OP_DUP11] = op_dup; - table[OP_DUP12] = op_dup; - table[OP_DUP13] = op_dup; - table[OP_DUP14] = op_dup; - table[OP_DUP15] = op_dup; - table[OP_DUP16] = op_dup; - - table[OP_SWAP1] = op_swap; - table[OP_SWAP2] = op_swap; - table[OP_SWAP3] = op_swap; - table[OP_SWAP4] = op_swap; - table[OP_SWAP5] = op_swap; - table[OP_SWAP6] = op_swap; - table[OP_SWAP7] = op_swap; - table[OP_SWAP8] = op_swap; - table[OP_SWAP9] = op_swap; - table[OP_SWAP10] = op_swap; - table[OP_SWAP11] = op_swap; - table[OP_SWAP12] = op_swap; - table[OP_SWAP13] = op_swap; - table[OP_SWAP14] = op_swap; - table[OP_SWAP15] = op_swap; - table[OP_SWAP16] = op_swap; - - table[OP_LOG0] = op_log; - table[OP_LOG1] = op_log; - table[OP_LOG2] = op_log; - table[OP_LOG3] = op_log; - table[OP_LOG4] = op_log; - - table[OP_CREATE] = op_create; - table[OP_CALL] = op_call; - table[OP_CALLCODE] = op_call; - table[OP_RETURN] = op_return; - table[OP_INVALID] = op_invalid; - table[OP_SELFDESTRUCT] = op_selfdestruct; + table[op] = {op_push_full, 3, 0, 1}; + + table[OP_DUP1] = {op_dup, 3, 1, 1}; + table[OP_DUP2] = {op_dup, 3, 2, 1}; + table[OP_DUP3] = {op_dup, 3, 3, 1}; + table[OP_DUP4] = {op_dup, 3, 4, 1}; + table[OP_DUP5] = {op_dup, 3, 5, 1}; + table[OP_DUP6] = {op_dup, 3, 6, 1}; + table[OP_DUP7] = {op_dup, 3, 7, 1}; + table[OP_DUP8] = {op_dup, 3, 8, 1}; + table[OP_DUP9] = {op_dup, 3, 9, 1}; + table[OP_DUP10] = {op_dup, 3, 10, 1}; + table[OP_DUP11] = {op_dup, 3, 11, 1}; + table[OP_DUP12] = {op_dup, 3, 12, 1}; + table[OP_DUP13] = {op_dup, 3, 13, 1}; + table[OP_DUP14] = {op_dup, 3, 14, 1}; + table[OP_DUP15] = {op_dup, 3, 15, 1}; + table[OP_DUP16] = {op_dup, 3, 16, 1}; + + table[OP_SWAP1] = {op_swap, 3, 2, 0}; + table[OP_SWAP2] = {op_swap, 3, 3, 0}; + table[OP_SWAP3] = {op_swap, 3, 4, 0}; + table[OP_SWAP4] = {op_swap, 3, 5, 0}; + table[OP_SWAP5] = {op_swap, 3, 6, 0}; + table[OP_SWAP6] = {op_swap, 3, 7, 0}; + table[OP_SWAP7] = {op_swap, 3, 8, 0}; + table[OP_SWAP8] = {op_swap, 3, 9, 0}; + table[OP_SWAP9] = {op_swap, 3, 10, 0}; + table[OP_SWAP10] = {op_swap, 3, 11, 0}; + table[OP_SWAP11] = {op_swap, 3, 12, 0}; + table[OP_SWAP12] = {op_swap, 3, 13, 0}; + table[OP_SWAP13] = {op_swap, 3, 14, 0}; + table[OP_SWAP14] = {op_swap, 3, 15, 0}; + table[OP_SWAP15] = {op_swap, 3, 16, 0}; + table[OP_SWAP16] = {op_swap, 3, 17, 0}; + + table[OP_LOG0] = {op_log, 1 * 375, 2, -2}; + table[OP_LOG1] = {op_log, 2 * 375, 3, -3}; + table[OP_LOG2] = {op_log, 3 * 375, 4, -4}; + table[OP_LOG3] = {op_log, 4 * 375, 5, -5}; + table[OP_LOG4] = {op_log, 5 * 375, 6, -6}; + + table[OP_CREATE] = {op_create, 32000, 3, -2}; + table[OP_CALL] = {op_call, 40, 7, -6}; + table[OP_CALLCODE] = {op_call, 40, 7, -6}; + table[OP_RETURN] = {op_return, 0, 2, -2}; + table[OP_INVALID] = {op_invalid, 0, 0, 0}; + table[OP_SELFDESTRUCT] = {op_selfdestruct, 0, 1, -1}; return table; } -constexpr exec_fn_table create_op_table_homestead() noexcept +constexpr op_table create_op_table_homestead() noexcept { auto table = create_op_table_frontier(); - table[OP_DELEGATECALL] = op_delegatecall; + table[OP_DELEGATECALL] = {op_delegatecall, 40, 6, -5}; return table; } -constexpr exec_fn_table create_op_table_byzantium() noexcept +constexpr op_table create_op_table_tangerine_whistle() noexcept { auto table = create_op_table_homestead(); - table[OP_RETURNDATASIZE] = op_returndatasize; - table[OP_RETURNDATACOPY] = op_returndatacopy; - table[OP_STATICCALL] = op_staticcall; - table[OP_REVERT] = op_revert; + table[OP_BALANCE].gas_cost = 400; + table[OP_EXTCODESIZE].gas_cost = 700; + table[OP_EXTCODECOPY].gas_cost = 700; + table[OP_SLOAD].gas_cost = 200; + table[OP_CALL].gas_cost = 700; + table[OP_CALLCODE].gas_cost = 700; + table[OP_DELEGATECALL].gas_cost = 700; + table[OP_SELFDESTRUCT].gas_cost = 5000; + return table; +} + +constexpr op_table create_op_table_byzantium() noexcept +{ + auto table = create_op_table_tangerine_whistle(); + table[OP_RETURNDATASIZE] = {op_returndatasize, 2, 0, 1}; + table[OP_RETURNDATACOPY] = {op_returndatacopy, 3, 3, -3}; + table[OP_STATICCALL] = {op_staticcall, 700, 6, -5}; + table[OP_REVERT] = {op_revert, 0, 2, -2}; return table; } -constexpr exec_fn_table create_op_table_constantinople() noexcept +constexpr op_table create_op_table_constantinople() noexcept { auto table = create_op_table_byzantium(); - table[OP_SHL] = op_shl; - table[OP_SHR] = op_shr; - table[OP_SAR] = op_sar; - table[OP_EXTCODEHASH] = op_extcodehash; - table[OP_CREATE2] = op_create2; + table[OP_SHL] = {op_shl, 3, 2, -1}; + table[OP_SHR] = {op_shr, 3, 2, -1}; + table[OP_SAR] = {op_sar, 3, 2, -1}; + table[OP_EXTCODEHASH] = {op_extcodehash, 400, 1, 0}; + table[OP_CREATE2] = {op_create2, 32000, 4, -3}; return table; } -constexpr exec_fn_table create_op_table_istanbul() noexcept +constexpr op_table create_op_table_istanbul() noexcept { auto table = create_op_table_constantinople(); return table; } -constexpr exec_fn_table op_tables[] = { - create_op_table_frontier(), // Frontier - create_op_table_homestead(), // Homestead - create_op_table_homestead(), // Tangerine Whistle - create_op_table_homestead(), // Spurious Dragon - create_op_table_byzantium(), // Byzantium - create_op_table_constantinople(), // Constantinople - create_op_table_constantinople(), // Petersburg - create_op_table_istanbul(), // Istanbul +constexpr op_table op_tables[] = { + create_op_table_frontier(), // Frontier + create_op_table_homestead(), // Homestead + create_op_table_tangerine_whistle(), // Tangerine Whistle + create_op_table_tangerine_whistle(), // Spurious Dragon + create_op_table_byzantium(), // Byzantium + create_op_table_constantinople(), // Constantinople + create_op_table_constantinople(), // Petersburg + create_op_table_istanbul(), // Istanbul }; static_assert(sizeof(op_tables) / sizeof(op_tables[0]) > EVMC_MAX_REVISION, "op table entry missing for an EVMC revision"); } // namespace -EVMC_EXPORT const exec_fn_table& get_op_table(evmc_revision rev) noexcept +EVMC_EXPORT const op_table& get_op_table(evmc_revision rev) noexcept { return op_tables[rev]; } diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 41349b8e5f..f6280edaec 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -21,10 +21,11 @@ add_executable(evmone-unittests analysis_test.cpp bytecode_test.cpp evmone_test.cpp + op_table_test.cpp utils_test.cpp vm_loader_evmone.cpp ) -target_link_libraries(evmone-unittests PRIVATE evm-unittests evmone testutils GTest::gtest GTest::gtest_main) +target_link_libraries(evmone-unittests PRIVATE evm-unittests evmone testutils evmc::instructions GTest::gtest GTest::gtest_main) target_include_directories(evmone-unittests PRIVATE ${evmone_private_include_dir}) gtest_discover_tests(evmone-unittests TEST_PREFIX ${PROJECT_NAME}/unittests/) diff --git a/test/unittests/analysis_test.cpp b/test/unittests/analysis_test.cpp index ec06369fd7..a3311f75b8 100644 --- a/test/unittests/analysis_test.cpp +++ b/test/unittests/analysis_test.cpp @@ -11,7 +11,7 @@ using namespace evmone; constexpr auto rev = EVMC_BYZANTIUM; -const auto& op_table = get_op_table(rev); +const auto& op_tbl = get_op_table(rev); TEST(analysis, example1) { @@ -20,14 +20,14 @@ TEST(analysis, example1) ASSERT_EQ(analysis.instrs.size(), 8); - EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]); - EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_PUSH1]); - EXPECT_EQ(analysis.instrs[2].fn, op_table[OP_PUSH1]); - EXPECT_EQ(analysis.instrs[3].fn, op_table[OP_MSTORE8]); - EXPECT_EQ(analysis.instrs[4].fn, op_table[OP_MSIZE]); - EXPECT_EQ(analysis.instrs[5].fn, op_table[OP_PUSH1]); - EXPECT_EQ(analysis.instrs[6].fn, op_table[OP_SSTORE]); - EXPECT_EQ(analysis.instrs[7].fn, op_table[OP_STOP]); + EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OPX_BEGINBLOCK].fn); + EXPECT_EQ(analysis.instrs[1].fn, op_tbl[OP_PUSH1].fn); + EXPECT_EQ(analysis.instrs[2].fn, op_tbl[OP_PUSH1].fn); + EXPECT_EQ(analysis.instrs[3].fn, op_tbl[OP_MSTORE8].fn); + EXPECT_EQ(analysis.instrs[4].fn, op_tbl[OP_MSIZE].fn); + EXPECT_EQ(analysis.instrs[5].fn, op_tbl[OP_PUSH1].fn); + EXPECT_EQ(analysis.instrs[6].fn, op_tbl[OP_SSTORE].fn); + EXPECT_EQ(analysis.instrs[7].fn, op_tbl[OP_STOP].fn); const auto& block = analysis.instrs[0].arg.block; EXPECT_EQ(block.gas_cost, 14); @@ -41,11 +41,11 @@ TEST(analysis, stack_up_and_down) const auto analysis = analyze(rev, &code[0], code.size()); ASSERT_EQ(analysis.instrs.size(), 20); - EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]); - EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_DUP2]); - EXPECT_EQ(analysis.instrs[2].fn, op_table[OP_DUP1]); - EXPECT_EQ(analysis.instrs[8].fn, op_table[OP_POP]); - EXPECT_EQ(analysis.instrs[18].fn, op_table[OP_PUSH1]); + EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OPX_BEGINBLOCK].fn); + EXPECT_EQ(analysis.instrs[1].fn, op_tbl[OP_DUP2].fn); + EXPECT_EQ(analysis.instrs[2].fn, op_tbl[OP_DUP1].fn); + EXPECT_EQ(analysis.instrs[8].fn, op_tbl[OP_POP].fn); + EXPECT_EQ(analysis.instrs[18].fn, op_tbl[OP_PUSH1].fn); const auto& block = analysis.instrs[0].arg.block; EXPECT_EQ(block.gas_cost, 7 * 3 + 10 * 2 + 3); @@ -61,7 +61,7 @@ TEST(analysis, push) ASSERT_EQ(analysis.instrs.size(), 4); ASSERT_EQ(analysis.push_values.size(), 1); - EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]); + EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OPX_BEGINBLOCK].fn); EXPECT_EQ(analysis.instrs[1].arg.small_push_value, push_value); EXPECT_EQ(analysis.instrs[2].arg.push_value, &analysis.push_values[0]); EXPECT_EQ(analysis.push_values[0], intx::uint256{0xee} << 240); @@ -76,10 +76,10 @@ TEST(analysis, jumpdest_skip) auto analysis = evmone::analyze(rev, &code[0], code.size()); ASSERT_EQ(analysis.instrs.size(), 4); - EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]); - EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_STOP]); - EXPECT_EQ(analysis.instrs[2].fn, op_table[OP_JUMPDEST]); - EXPECT_EQ(analysis.instrs[3].fn, op_table[OP_STOP]); + EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OPX_BEGINBLOCK].fn); + EXPECT_EQ(analysis.instrs[1].fn, op_tbl[OP_STOP].fn); + EXPECT_EQ(analysis.instrs[2].fn, op_tbl[OP_JUMPDEST].fn); + EXPECT_EQ(analysis.instrs[3].fn, op_tbl[OP_STOP].fn); } TEST(analysis, jump1) @@ -102,8 +102,8 @@ TEST(analysis, empty) auto analysis = evmone::analyze(rev, &code[0], code.size()); ASSERT_EQ(analysis.instrs.size(), 2); - EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]); - EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_STOP]); + EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OPX_BEGINBLOCK].fn); + EXPECT_EQ(analysis.instrs[1].fn, op_tbl[OP_STOP].fn); } TEST(analysis, only_jumpdest) @@ -123,10 +123,10 @@ TEST(analysis, jumpi_at_the_end) auto analysis = evmone::analyze(rev, &code[0], code.size()); ASSERT_EQ(analysis.instrs.size(), 4); - EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]); - EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_JUMPI]); - EXPECT_EQ(analysis.instrs[2].fn, op_table[OPX_BEGINBLOCK]); - EXPECT_EQ(analysis.instrs[3].fn, op_table[OP_STOP]); + EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OPX_BEGINBLOCK].fn); + EXPECT_EQ(analysis.instrs[1].fn, op_tbl[OP_JUMPI].fn); + EXPECT_EQ(analysis.instrs[2].fn, op_tbl[OPX_BEGINBLOCK].fn); + EXPECT_EQ(analysis.instrs[3].fn, op_tbl[OP_STOP].fn); } TEST(analysis, terminated_last_block) @@ -137,10 +137,10 @@ TEST(analysis, terminated_last_block) auto analysis = evmone::analyze(rev, &code[0], code.size()); ASSERT_EQ(analysis.instrs.size(), 6); - EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]); - EXPECT_EQ(analysis.instrs[3].fn, op_table[OP_RETURN]); - EXPECT_EQ(analysis.instrs[4].fn, op_table[OPX_BEGINBLOCK]); - EXPECT_EQ(analysis.instrs[5].fn, op_table[OP_STOP]); + EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OPX_BEGINBLOCK].fn); + EXPECT_EQ(analysis.instrs[3].fn, op_tbl[OP_RETURN].fn); + EXPECT_EQ(analysis.instrs[4].fn, op_tbl[OPX_BEGINBLOCK].fn); + EXPECT_EQ(analysis.instrs[5].fn, op_tbl[OP_STOP].fn); } TEST(analysis, jumpdests_groups) @@ -149,17 +149,17 @@ TEST(analysis, jumpdests_groups) auto analysis = evmone::analyze(rev, &code[0], code.size()); ASSERT_EQ(analysis.instrs.size(), 11); - EXPECT_EQ(analysis.instrs[0].fn, op_table[OP_JUMPDEST]); - EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_JUMPDEST]); - EXPECT_EQ(analysis.instrs[2].fn, op_table[OP_JUMPDEST]); - EXPECT_EQ(analysis.instrs[3].fn, op_table[OP_PUSH1]); - EXPECT_EQ(analysis.instrs[4].fn, op_table[OP_JUMPDEST]); - EXPECT_EQ(analysis.instrs[5].fn, op_table[OP_JUMPDEST]); - EXPECT_EQ(analysis.instrs[6].fn, op_table[OP_JUMPDEST]); - EXPECT_EQ(analysis.instrs[7].fn, op_table[OP_PUSH1]); - EXPECT_EQ(analysis.instrs[8].fn, op_table[OP_JUMPI]); - EXPECT_EQ(analysis.instrs[9].fn, op_table[OPX_BEGINBLOCK]); - EXPECT_EQ(analysis.instrs[10].fn, op_table[OP_STOP]); + EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OP_JUMPDEST].fn); + EXPECT_EQ(analysis.instrs[1].fn, op_tbl[OP_JUMPDEST].fn); + EXPECT_EQ(analysis.instrs[2].fn, op_tbl[OP_JUMPDEST].fn); + EXPECT_EQ(analysis.instrs[3].fn, op_tbl[OP_PUSH1].fn); + EXPECT_EQ(analysis.instrs[4].fn, op_tbl[OP_JUMPDEST].fn); + EXPECT_EQ(analysis.instrs[5].fn, op_tbl[OP_JUMPDEST].fn); + EXPECT_EQ(analysis.instrs[6].fn, op_tbl[OP_JUMPDEST].fn); + EXPECT_EQ(analysis.instrs[7].fn, op_tbl[OP_PUSH1].fn); + EXPECT_EQ(analysis.instrs[8].fn, op_tbl[OP_JUMPI].fn); + EXPECT_EQ(analysis.instrs[9].fn, op_tbl[OPX_BEGINBLOCK].fn); + EXPECT_EQ(analysis.instrs[10].fn, op_tbl[OP_STOP].fn); ASSERT_EQ(analysis.jumpdest_offsets.size(), 6); diff --git a/test/unittests/op_table_test.cpp b/test/unittests/op_table_test.cpp new file mode 100644 index 0000000000..3ce4ee6945 --- /dev/null +++ b/test/unittests/op_table_test.cpp @@ -0,0 +1,30 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2019 The evmone Authors. +// Licensed under the Apache License, Version 2.0. + +#include +#include +#include + +TEST(op_table, compare_with_evmc_instruction_tables) +{ + for (int r = EVMC_FRONTIER; r <= EVMC_MAX_REVISION; ++r) + { + const auto rev = static_cast(r); + const auto& evmone_tbl = evmone::get_op_table(rev); + const auto* evmc_tbl = evmc_get_instruction_metrics_table(rev); + + for (size_t i = 0; i < evmone_tbl.size(); ++i) + { + const auto& metrics = evmone_tbl[i]; + const auto& ref_metrics = evmc_tbl[i]; + + // Compare gas costs. Normalize -1 values in EVMC for undefined instructions. + EXPECT_EQ(metrics.gas_cost, std::max(ref_metrics.gas_cost, int16_t{0})); + + EXPECT_EQ(metrics.stack_req, ref_metrics.num_stack_arguments); + EXPECT_EQ(metrics.stack_change, + ref_metrics.num_stack_returned_items - ref_metrics.num_stack_arguments); + } + } +}