diff --git a/lib/evmone/CMakeLists.txt b/lib/evmone/CMakeLists.txt index 659bcc19e4..777c0800f2 100644 --- a/lib/evmone/CMakeLists.txt +++ b/lib/evmone/CMakeLists.txt @@ -11,8 +11,9 @@ add_library(evmone advanced_execution.cpp advanced_execution.hpp advanced_instructions.cpp - baseline.cpp baseline.hpp + baseline_analysis.cpp + baseline_execution.cpp baseline_instruction_table.cpp baseline_instruction_table.hpp constants.hpp diff --git a/lib/evmone/baseline.hpp b/lib/evmone/baseline.hpp index cc22da25b4..fbf6d62e20 100644 --- a/lib/evmone/baseline.hpp +++ b/lib/evmone/baseline.hpp @@ -42,10 +42,6 @@ class CodeAnalysis : executable_code{code}, eof_header{std::move(header)} {} }; -static_assert(std::is_move_constructible_v); -static_assert(std::is_move_assignable_v); -static_assert(!std::is_copy_constructible_v); -static_assert(!std::is_copy_assignable_v); /// Analyze the code to build the bitmap of valid JUMPDEST locations. EVMC_EXPORT CodeAnalysis analyze(evmc_revision rev, bytes_view code); diff --git a/lib/evmone/baseline_analysis.cpp b/lib/evmone/baseline_analysis.cpp new file mode 100644 index 0000000000..85efe8f2a4 --- /dev/null +++ b/lib/evmone/baseline_analysis.cpp @@ -0,0 +1,81 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2020 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "baseline.hpp" +#include "eof.hpp" +#include "instructions.hpp" +#include + +namespace evmone::baseline +{ +static_assert(std::is_move_constructible_v); +static_assert(std::is_move_assignable_v); +static_assert(!std::is_copy_constructible_v); +static_assert(!std::is_copy_assignable_v); + +namespace +{ +CodeAnalysis::JumpdestMap analyze_jumpdests(bytes_view code) +{ + // 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(op) <= OP_PUSH32 is always true and can be skipped. + static_assert(OP_PUSH32 == std::numeric_limits::max()); + + CodeAnalysis::JumpdestMap map(code.size()); // Allocate and init bitmap with zeros. + for (size_t i = 0; i < code.size(); ++i) + { + const auto op = code[i]; + if (static_cast(op) >= OP_PUSH1) // If any PUSH opcode (see explanation above). + i += op - size_t{OP_PUSH1 - 1}; // Skip PUSH data. + else if (INTX_UNLIKELY(op == OP_JUMPDEST)) + map[i] = true; + } + + return map; +} + +std::unique_ptr pad_code(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; + + auto padded_code = std::make_unique_for_overwrite(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}); + return padded_code; +} + + +CodeAnalysis analyze_legacy(bytes_view code) +{ + // TODO: The padded code buffer and jumpdest bitmap can be created with single allocation. + return {pad_code(code), code.size(), analyze_jumpdests(code)}; +} + +CodeAnalysis analyze_eof1(bytes_view container) +{ + auto header = read_valid_eof1_header(container); + + // Extract all code sections as single buffer reference. + // TODO: It would be much easier if header had code_sections_offset and data_section_offset + // with code_offsets[] being relative to code_sections_offset. + const auto code_sections_offset = header.code_offsets[0]; + const auto code_sections_end = size_t{header.code_offsets.back()} + header.code_sizes.back(); + const auto executable_code = + container.substr(code_sections_offset, code_sections_end - code_sections_offset); + + return CodeAnalysis{executable_code, std::move(header)}; +} +} // namespace + +CodeAnalysis analyze(evmc_revision rev, bytes_view code) +{ + if (rev < EVMC_PRAGUE || !is_eof_container(code)) + return analyze_legacy(code); + return analyze_eof1(code); +} +} // namespace evmone::baseline diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline_execution.cpp similarity index 84% rename from lib/evmone/baseline.cpp rename to lib/evmone/baseline_execution.cpp index 130070dee4..4f9a4b5828 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline_execution.cpp @@ -24,71 +24,6 @@ namespace evmone::baseline { -namespace -{ -CodeAnalysis::JumpdestMap analyze_jumpdests(bytes_view code) -{ - // 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(op) <= OP_PUSH32 is always true and can be skipped. - static_assert(OP_PUSH32 == std::numeric_limits::max()); - - CodeAnalysis::JumpdestMap map(code.size()); // Allocate and init bitmap with zeros. - for (size_t i = 0; i < code.size(); ++i) - { - const auto op = code[i]; - if (static_cast(op) >= OP_PUSH1) // If any PUSH opcode (see explanation above). - i += op - size_t{OP_PUSH1 - 1}; // Skip PUSH data. - else if (INTX_UNLIKELY(op == OP_JUMPDEST)) - map[i] = true; - } - - return map; -} - -std::unique_ptr pad_code(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; - - auto padded_code = std::make_unique_for_overwrite(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}); - return padded_code; -} - - -CodeAnalysis analyze_legacy(bytes_view code) -{ - // TODO: The padded code buffer and jumpdest bitmap can be created with single allocation. - return {pad_code(code), code.size(), analyze_jumpdests(code)}; -} - -CodeAnalysis analyze_eof1(bytes_view container) -{ - auto header = read_valid_eof1_header(container); - - // Extract all code sections as single buffer reference. - // TODO: It would be much easier if header had code_sections_offset and data_section_offset - // with code_offsets[] being relative to code_sections_offset. - const auto code_sections_offset = header.code_offsets[0]; - const auto code_sections_end = size_t{header.code_offsets.back()} + header.code_sizes.back(); - const auto executable_code = - container.substr(code_sections_offset, code_sections_end - code_sections_offset); - - return CodeAnalysis{executable_code, std::move(header)}; -} -} // namespace - -CodeAnalysis analyze(evmc_revision rev, bytes_view code) -{ - if (rev < EVMC_PRAGUE || !is_eof_container(code)) - return analyze_legacy(code); - return analyze_eof1(code); -} - namespace { /// Checks instruction requirements before execution.