diff --git a/lib/evmone/analysis.cpp b/lib/evmone/analysis.cpp index b7cea2a910..bb360d1904 100644 --- a/lib/evmone/analysis.cpp +++ b/lib/evmone/analysis.cpp @@ -17,17 +17,6 @@ bool is_terminator(uint8_t c) noexcept } } // namespace -int code_analysis::find_jumpdest(int offset) const noexcept -{ - // TODO: Replace with lower_bound(). - for (const auto& d : jumpdest_map) - { - if (d.first == offset) - return d.second; - } - return -1; -} - evmc_call_kind op2call_kind(uint8_t opcode) noexcept { switch (opcode) @@ -76,7 +65,10 @@ code_analysis analyze( beginblock_instr.arg.p.number = static_cast(analysis.blocks.size() - 1); if (jumpdest) // Add the jumpdest to the map. - analysis.jumpdest_map.emplace_back(static_cast(i), instr_index); + { + analysis.jumpdest_offsets.emplace_back(static_cast(i)); + analysis.jumpdest_targets.emplace_back(static_cast(instr_index)); + } else // Increase instruction count because additional BEGINBLOCK was injected. ++instr_index; } diff --git a/lib/evmone/analysis.hpp b/lib/evmone/analysis.hpp index ef15eda4bb..92c57caf06 100644 --- a/lib/evmone/analysis.hpp +++ b/lib/evmone/analysis.hpp @@ -152,12 +152,25 @@ struct code_analysis /// invalidated when the container grows. std::deque args_storage; - std::vector> jumpdest_map; - - // TODO: Exported for unit tests. Rework unit tests? - EVMC_EXPORT int find_jumpdest(int offset) const noexcept; + /// The offsets of JUMPDESTs in the original code. + /// These are values that JUMP/JUMPI receives as an argument. + /// The elements are sorted. + std::vector jumpdest_offsets; + + /// The indexes of the instructions in the generated instruction table + /// matching the elements from jumdest_offsets. + /// This is value to which the next instruction pointer must be set in JUMP/JUMPI. + std::vector jumpdest_targets; }; +inline int find_jumpdest(const code_analysis& analysis, int offset) noexcept +{ + const auto begin = std::begin(analysis.jumpdest_offsets); + const auto end = std::end(analysis.jumpdest_offsets); + const auto it = std::lower_bound(begin, end, offset); + return (it != end && *it == offset) ? analysis.jumpdest_targets[it - begin] : -1; +} + EVMC_EXPORT code_analysis analyze( const exec_fn_table& fns, evmc_revision rev, const uint8_t* code, size_t code_size) noexcept; diff --git a/lib/evmone/instructions.cpp b/lib/evmone/instructions.cpp index 55df30b0c8..8dbade886f 100644 --- a/lib/evmone/instructions.cpp +++ b/lib/evmone/instructions.cpp @@ -481,7 +481,7 @@ void op_jump(execution_state& state, instr_argument) noexcept const auto dst = state.stack.pop(); auto pc = -1; if (std::numeric_limits::max() < dst || - (pc = state.analysis->find_jumpdest(static_cast(dst))) < 0) + (pc = find_jumpdest(*state.analysis, static_cast(dst))) < 0) return state.exit(EVMC_BAD_JUMP_DESTINATION); state.next_instr = &state.analysis->instrs[static_cast(pc)]; diff --git a/test/unittests/analysis_test.cpp b/test/unittests/analysis_test.cpp index d2baf43b01..9401bdad26 100644 --- a/test/unittests/analysis_test.cpp +++ b/test/unittests/analysis_test.cpp @@ -86,10 +86,13 @@ TEST(analysis, jump1) const auto analysis = analyze(fake_fn_table, rev, &code[0], code.size()); ASSERT_EQ(analysis.blocks.size(), 3); - ASSERT_EQ(analysis.jumpdest_map.size(), 1); - EXPECT_EQ(analysis.jumpdest_map[0], std::pair(6, 5)); - EXPECT_EQ(analysis.find_jumpdest(6), 5); - EXPECT_EQ(analysis.find_jumpdest(0), -1); + ASSERT_EQ(analysis.jumpdest_offsets.size(), 1); + ASSERT_EQ(analysis.jumpdest_targets.size(), 1); + EXPECT_EQ(analysis.jumpdest_offsets[0], 6); + EXPECT_EQ(analysis.jumpdest_targets[0], 5); + EXPECT_EQ(find_jumpdest(analysis, 6), 5); + EXPECT_EQ(find_jumpdest(analysis, 0), -1); + EXPECT_EQ(find_jumpdest(analysis, 7), -1); } TEST(analysis, empty) @@ -108,8 +111,10 @@ TEST(analysis, only_jumpdest) auto analysis = evmone::analyze(fake_fn_table, rev, &code[0], code.size()); ASSERT_EQ(analysis.blocks.size(), 1); - ASSERT_EQ(analysis.jumpdest_map.size(), 1); - EXPECT_EQ(analysis.jumpdest_map[0], std::pair(0, 0)); + ASSERT_EQ(analysis.jumpdest_offsets.size(), 1); + ASSERT_EQ(analysis.jumpdest_targets.size(), 1); + EXPECT_EQ(analysis.jumpdest_offsets[0], 0); + EXPECT_EQ(analysis.jumpdest_targets[0], 0); } TEST(analysis, jumpi_at_the_end) diff --git a/test/utils/dump.cpp b/test/utils/dump.cpp index 735019bb9f..ce5113d1bc 100644 --- a/test/utils/dump.cpp +++ b/test/utils/dump.cpp @@ -30,14 +30,14 @@ void dump_analysis(const evmone::code_analysis& analysis) { block = &analysis.blocks[size_t(instr.arg.p.number)]; - auto get_jumpdest_offset = [&analysis](size_t index) noexcept + const auto get_jumpdest_offset = [&analysis](size_t index) noexcept { - for (const auto& d : analysis.jumpdest_map) + for (size_t t = 0; t < analysis.jumpdest_targets.size(); ++t) { - if (d.second == static_cast(index)) - return d.first; + if (t == index) + return analysis.jumpdest_offsets[t]; } - return -1; + return int16_t{-1}; }; std::cout << "┌ ";