Skip to content

Commit 597e658

Browse files
authored
Merge pull request #80 from ethereum/jump
Speed up finding jumpdests
2 parents e2f0e7c + e4bd497 commit 597e658

File tree

5 files changed

+38
-28
lines changed

5 files changed

+38
-28
lines changed

lib/evmone/analysis.cpp

+4-12
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,6 @@ bool is_terminator(uint8_t c) noexcept
1717
}
1818
} // namespace
1919

20-
int code_analysis::find_jumpdest(int offset) const noexcept
21-
{
22-
// TODO: Replace with lower_bound().
23-
for (const auto& d : jumpdest_map)
24-
{
25-
if (d.first == offset)
26-
return d.second;
27-
}
28-
return -1;
29-
}
30-
3120
evmc_call_kind op2call_kind(uint8_t opcode) noexcept
3221
{
3322
switch (opcode)
@@ -76,7 +65,10 @@ code_analysis analyze(
7665
beginblock_instr.arg.p.number = static_cast<int>(analysis.blocks.size() - 1);
7766

7867
if (jumpdest) // Add the jumpdest to the map.
79-
analysis.jumpdest_map.emplace_back(static_cast<int>(i), instr_index);
68+
{
69+
analysis.jumpdest_offsets.emplace_back(static_cast<int16_t>(i));
70+
analysis.jumpdest_targets.emplace_back(static_cast<int16_t>(instr_index));
71+
}
8072
else // Increase instruction count because additional BEGINBLOCK was injected.
8173
++instr_index;
8274
}

lib/evmone/analysis.hpp

+17-4
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,25 @@ struct code_analysis
152152
/// invalidated when the container grows.
153153
std::deque<bytes32> args_storage;
154154

155-
std::vector<std::pair<int, int>> jumpdest_map;
156-
157-
// TODO: Exported for unit tests. Rework unit tests?
158-
EVMC_EXPORT int find_jumpdest(int offset) const noexcept;
155+
/// The offsets of JUMPDESTs in the original code.
156+
/// These are values that JUMP/JUMPI receives as an argument.
157+
/// The elements are sorted.
158+
std::vector<int16_t> jumpdest_offsets;
159+
160+
/// The indexes of the instructions in the generated instruction table
161+
/// matching the elements from jumdest_offsets.
162+
/// This is value to which the next instruction pointer must be set in JUMP/JUMPI.
163+
std::vector<int16_t> jumpdest_targets;
159164
};
160165

166+
inline int find_jumpdest(const code_analysis& analysis, int offset) noexcept
167+
{
168+
const auto begin = std::begin(analysis.jumpdest_offsets);
169+
const auto end = std::end(analysis.jumpdest_offsets);
170+
const auto it = std::lower_bound(begin, end, offset);
171+
return (it != end && *it == offset) ? analysis.jumpdest_targets[it - begin] : -1;
172+
}
173+
161174
EVMC_EXPORT code_analysis analyze(
162175
const exec_fn_table& fns, evmc_revision rev, const uint8_t* code, size_t code_size) noexcept;
163176

lib/evmone/instructions.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ void op_jump(execution_state& state, instr_argument) noexcept
481481
const auto dst = state.stack.pop();
482482
auto pc = -1;
483483
if (std::numeric_limits<int>::max() < dst ||
484-
(pc = state.analysis->find_jumpdest(static_cast<int>(dst))) < 0)
484+
(pc = find_jumpdest(*state.analysis, static_cast<int>(dst))) < 0)
485485
return state.exit(EVMC_BAD_JUMP_DESTINATION);
486486

487487
state.next_instr = &state.analysis->instrs[static_cast<size_t>(pc)];

test/unittests/analysis_test.cpp

+11-6
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,13 @@ TEST(analysis, jump1)
8686
const auto analysis = analyze(fake_fn_table, rev, &code[0], code.size());
8787

8888
ASSERT_EQ(analysis.blocks.size(), 3);
89-
ASSERT_EQ(analysis.jumpdest_map.size(), 1);
90-
EXPECT_EQ(analysis.jumpdest_map[0], std::pair(6, 5));
91-
EXPECT_EQ(analysis.find_jumpdest(6), 5);
92-
EXPECT_EQ(analysis.find_jumpdest(0), -1);
89+
ASSERT_EQ(analysis.jumpdest_offsets.size(), 1);
90+
ASSERT_EQ(analysis.jumpdest_targets.size(), 1);
91+
EXPECT_EQ(analysis.jumpdest_offsets[0], 6);
92+
EXPECT_EQ(analysis.jumpdest_targets[0], 5);
93+
EXPECT_EQ(find_jumpdest(analysis, 6), 5);
94+
EXPECT_EQ(find_jumpdest(analysis, 0), -1);
95+
EXPECT_EQ(find_jumpdest(analysis, 7), -1);
9396
}
9497

9598
TEST(analysis, empty)
@@ -108,8 +111,10 @@ TEST(analysis, only_jumpdest)
108111
auto analysis = evmone::analyze(fake_fn_table, rev, &code[0], code.size());
109112

110113
ASSERT_EQ(analysis.blocks.size(), 1);
111-
ASSERT_EQ(analysis.jumpdest_map.size(), 1);
112-
EXPECT_EQ(analysis.jumpdest_map[0], std::pair(0, 0));
114+
ASSERT_EQ(analysis.jumpdest_offsets.size(), 1);
115+
ASSERT_EQ(analysis.jumpdest_targets.size(), 1);
116+
EXPECT_EQ(analysis.jumpdest_offsets[0], 0);
117+
EXPECT_EQ(analysis.jumpdest_targets[0], 0);
113118
}
114119

115120
TEST(analysis, jumpi_at_the_end)

test/utils/dump.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ void dump_analysis(const evmone::code_analysis& analysis)
3030
{
3131
block = &analysis.blocks[size_t(instr.arg.p.number)];
3232

33-
auto get_jumpdest_offset = [&analysis](size_t index) noexcept
33+
const auto get_jumpdest_offset = [&analysis](size_t index) noexcept
3434
{
35-
for (const auto& d : analysis.jumpdest_map)
35+
for (size_t t = 0; t < analysis.jumpdest_targets.size(); ++t)
3636
{
37-
if (d.second == static_cast<int>(index))
38-
return d.first;
37+
if (t == index)
38+
return analysis.jumpdest_offsets[t];
3939
}
40-
return -1;
40+
return int16_t{-1};
4141
};
4242

4343
std::cout << "";

0 commit comments

Comments
 (0)