Skip to content

Commit

Permalink
Implement EIP-4750 analysis in baseline
Browse files Browse the repository at this point in the history
Perform jumpdest analysis for every code section and save all results.
  • Loading branch information
gumb0 committed Sep 12, 2022
1 parent 41acfad commit 6fb0693
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 20 deletions.
45 changes: 27 additions & 18 deletions lib/evmone/baseline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,8 @@ namespace evmone::baseline
{
namespace
{
std::pair<std::unique_ptr<uint8_t[]>, CodeAnalysis::JumpdestMap> analyze_jumpdests(bytes_view code)
CodeAnalysis::JumpdestMap analyze_jumpdests(bytes_view code)
{
// We need at most 33 bytes of code padding: 32 for possible missing all data bytes of PUSH32
// at the very end of the code; and one more byte for STOP to guarantee there is a terminating
// instruction at the code end.
constexpr auto padding = 32 + 1;

// To find if op is any PUSH opcode (OP_PUSH1 <= op <= OP_PUSH32)
// it can be noticed that OP_PUSH32 is INT8_MAX (0x7f) therefore
// static_cast<int8_t>(op) <= OP_PUSH32 is always true and can be skipped.
Expand All @@ -49,29 +44,43 @@ std::pair<std::unique_ptr<uint8_t[]>, CodeAnalysis::JumpdestMap> analyze_jumpdes
else if (INTX_UNLIKELY(op == OP_JUMPDEST))
map[i] = true;
}
return map;
}

CodeAnalysis analyze_legacy(bytes_view code)
{
// We need at most 33 bytes of code padding: 32 for possible missing all data bytes of PUSH32
// at the very end of the code; and one more byte for STOP to guarantee there is a terminating
// instruction at the code end.
constexpr auto padding = 32 + 1;

// To find if op is any PUSH opcode (OP_PUSH1 <= op <= OP_PUSH32)
// it can be noticed that OP_PUSH32 is INT8_MAX (0x7f) therefore
// static_cast<int8_t>(op) <= OP_PUSH32 is always true and can be skipped.
static_assert(OP_PUSH32 == std::numeric_limits<int8_t>::max());

// Using "raw" new operator instead of std::make_unique() to get uninitialized array.
std::unique_ptr<uint8_t[]> padded_code{new uint8_t[code.size() + padding]};
std::copy(std::begin(code), std::end(code), padded_code.get());
std::fill_n(&padded_code[code.size()], padding, uint8_t{OP_STOP});

// TODO: The padded code buffer and jumpdest bitmap can be created with single allocation.
return std::make_pair(std::move(padded_code), std::move(map));
return {std::move(padded_code), {analyze_jumpdests(code)}, {}, true};
}


CodeAnalysis analyze_legacy(bytes_view code)
CodeAnalysis analyze_eof1(bytes_view container, const EOF1Header& header)
{
auto code_and_jumpdest_map = analyze_jumpdests(code);
return {std::move(code_and_jumpdest_map.first), std::move(code_and_jumpdest_map.second), true};
std::vector<CodeAnalysis::JumpdestMap> jumpdest_maps;
std::vector<bytes_view> codes;
for (size_t i = 0; i < header.code_offsets.size(); ++i)
{
codes.emplace_back(&container[header.code_offsets[i]], header.code_sizes[i]);
const auto jumpdest_map = analyze_jumpdests(codes.back());
jumpdest_maps.emplace_back(jumpdest_map);
}
return {{}, jumpdest_maps, codes, false};
}

CodeAnalysis analyze_eof1(bytes_view::const_iterator code, const EOF1Header& header)
{
auto code_and_jumpdest_map =
analyze_jumpdests({&code[header.code_begin(0)], header.code_sizes[0]});
return {std::move(code_and_jumpdest_map.first), std::move(code_and_jumpdest_map.second), false};
}
} // namespace

CodeAnalysis analyze(evmc_revision rev, bytes_view code)
Expand All @@ -80,7 +89,7 @@ CodeAnalysis analyze(evmc_revision rev, bytes_view code)
return analyze_legacy(code);

const auto eof1_header = read_valid_eof1_header(code.begin());
return analyze_eof1(code.begin(), eof1_header);
return analyze_eof1(code, eof1_header);
}

namespace
Expand Down
3 changes: 2 additions & 1 deletion lib/evmone/baseline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ struct CodeAnalysis
using JumpdestMap = std::vector<bool>;

std::unique_ptr<uint8_t[]> padded_code;
JumpdestMap jumpdest_map;
std::vector<JumpdestMap> jumpdest_maps;
std::vector<bytes_view> codes;
bool is_legacy_code = false;
};
static_assert(std::is_move_constructible_v<CodeAnalysis>);
Expand Down
3 changes: 2 additions & 1 deletion lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,8 @@ inline evmc_status_code sstore(StackTop stack, ExecutionState& state) noexcept
/// Internal jump implementation for JUMP/JUMPI instructions.
inline code_iterator jump_impl(ExecutionState& state, const uint256& dst) noexcept
{
const auto& jumpdest_map = state.analysis.baseline->jumpdest_map;
// TODO get section index from ExecutionState
const auto& jumpdest_map = state.analysis.baseline->jumpdest_maps[0];
if (dst >= jumpdest_map.size() || !jumpdest_map[static_cast<size_t>(dst)])
{
state.status = EVMC_BAD_JUMP_DESTINATION;
Expand Down

0 comments on commit 6fb0693

Please sign in to comment.