Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize Baseline interpreter by using padded code #315

Merged
merged 4 commits into from
Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 57 additions & 43 deletions lib/evmone/baseline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,27 @@ CodeAnalysis analyze(const uint8_t* code, size_t code_size)
static_assert(OP_PUSH32 == std::numeric_limits<int8_t>::max());

CodeAnalysis::JumpdestMap map(code_size); // Allocate and init bitmap with zeros.
for (size_t i = 0; i < code_size; ++i)
size_t i = 0;
while (i < code_size)
{
const auto op = code[i];
if (static_cast<int8_t>(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;
++i;
}
return CodeAnalysis{std::move(map)};

// i is the needed code size including the last push data (can be bigger than code_size).
chfast marked this conversation as resolved.
Show resolved Hide resolved
// Using "raw" new operator instead of std::make_unique() to get uninitialized array.
std::unique_ptr<uint8_t[]> padded_code{new uint8_t[i + 1]}; // +1 for the final STOP.
std::copy_n(code, code_size, padded_code.get());
padded_code[i] = OP_STOP; // Set final STOP at the code end.

// TODO: Using fixed-size padding of 33, the padded code buffer and jumpdest bitmap can be
// created with single allocation.

return CodeAnalysis{std::move(padded_code), std::move(map)};
}

namespace
Expand All @@ -46,14 +58,10 @@ const uint8_t* op_jump(
}

template <size_t Len>
inline const uint8_t* load_push(
ExecutionState& state, const uint8_t* code, const uint8_t* code_end) noexcept
inline const uint8_t* load_push(ExecutionState& state, const uint8_t* code) noexcept
{
// TODO: Also last full push can be ignored.
if (code + Len > code_end) // Trimmed push data can be ignored.
return code_end;

uint8_t buffer[Len];
// This valid because code is padded with garbage to satisfy push data read pass the code end.
std::memcpy(buffer, code, Len);
state.stack.push(intx::be::load<intx::uint256>(buffer));
return code + Len;
Expand Down Expand Up @@ -99,6 +107,9 @@ inline evmc_status_code check_requirements(const char* const* instruction_names,
template <bool TracingEnabled>
evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& analysis) noexcept
{
// Use padded code.
state.code = {analysis.padded_code.get(), state.code.size()};

auto* tracer = vm.get_tracer();
if constexpr (TracingEnabled)
tracer->notify_execution_start(state.rev, *state.msg, state.code);
Expand All @@ -107,12 +118,15 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana
const auto instruction_metrics = evmc_get_instruction_metrics_table(state.rev);

const auto* const code = state.code.data();
const auto* const code_end = code + state.code.size();
auto pc = code;
while (pc != code_end)
while (true) // Guaranteed to terminate because padded code ends with STOP.
{
if constexpr (TracingEnabled)
tracer->notify_instruction_start(static_cast<uint32_t>(pc - code), state);
{
const auto offset = static_cast<uint32_t>(pc - code);
if (offset < state.code.size()) // Skip STOP from code padding.
tracer->notify_instruction_start(offset, state);
}

const auto op = *pc;
const auto status = check_requirements(instruction_names, instruction_metrics, state, op);
Expand Down Expand Up @@ -429,100 +443,100 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana
break;

case OP_PUSH1:
pc = load_push<1>(state, pc + 1, code_end);
pc = load_push<1>(state, pc + 1);
continue;
case OP_PUSH2:
pc = load_push<2>(state, pc + 1, code_end);
pc = load_push<2>(state, pc + 1);
continue;
case OP_PUSH3:
pc = load_push<3>(state, pc + 1, code_end);
pc = load_push<3>(state, pc + 1);
continue;
case OP_PUSH4:
pc = load_push<4>(state, pc + 1, code_end);
pc = load_push<4>(state, pc + 1);
continue;
case OP_PUSH5:
pc = load_push<5>(state, pc + 1, code_end);
pc = load_push<5>(state, pc + 1);
continue;
case OP_PUSH6:
pc = load_push<6>(state, pc + 1, code_end);
pc = load_push<6>(state, pc + 1);
continue;
case OP_PUSH7:
pc = load_push<7>(state, pc + 1, code_end);
pc = load_push<7>(state, pc + 1);
continue;
case OP_PUSH8:
pc = load_push<8>(state, pc + 1, code_end);
pc = load_push<8>(state, pc + 1);
continue;
case OP_PUSH9:
pc = load_push<9>(state, pc + 1, code_end);
pc = load_push<9>(state, pc + 1);
continue;
case OP_PUSH10:
pc = load_push<10>(state, pc + 1, code_end);
pc = load_push<10>(state, pc + 1);
continue;
case OP_PUSH11:
pc = load_push<11>(state, pc + 1, code_end);
pc = load_push<11>(state, pc + 1);
continue;
case OP_PUSH12:
pc = load_push<12>(state, pc + 1, code_end);
pc = load_push<12>(state, pc + 1);
continue;
case OP_PUSH13:
pc = load_push<13>(state, pc + 1, code_end);
pc = load_push<13>(state, pc + 1);
continue;
case OP_PUSH14:
pc = load_push<14>(state, pc + 1, code_end);
pc = load_push<14>(state, pc + 1);
continue;
case OP_PUSH15:
pc = load_push<15>(state, pc + 1, code_end);
pc = load_push<15>(state, pc + 1);
continue;
case OP_PUSH16:
pc = load_push<16>(state, pc + 1, code_end);
pc = load_push<16>(state, pc + 1);
continue;
case OP_PUSH17:
pc = load_push<17>(state, pc + 1, code_end);
pc = load_push<17>(state, pc + 1);
continue;
case OP_PUSH18:
pc = load_push<18>(state, pc + 1, code_end);
pc = load_push<18>(state, pc + 1);
continue;
case OP_PUSH19:
pc = load_push<19>(state, pc + 1, code_end);
pc = load_push<19>(state, pc + 1);
continue;
case OP_PUSH20:
pc = load_push<20>(state, pc + 1, code_end);
pc = load_push<20>(state, pc + 1);
continue;
case OP_PUSH21:
pc = load_push<21>(state, pc + 1, code_end);
pc = load_push<21>(state, pc + 1);
continue;
case OP_PUSH22:
pc = load_push<22>(state, pc + 1, code_end);
pc = load_push<22>(state, pc + 1);
continue;
case OP_PUSH23:
pc = load_push<23>(state, pc + 1, code_end);
pc = load_push<23>(state, pc + 1);
continue;
case OP_PUSH24:
pc = load_push<24>(state, pc + 1, code_end);
pc = load_push<24>(state, pc + 1);
continue;
case OP_PUSH25:
pc = load_push<25>(state, pc + 1, code_end);
pc = load_push<25>(state, pc + 1);
continue;
case OP_PUSH26:
pc = load_push<26>(state, pc + 1, code_end);
pc = load_push<26>(state, pc + 1);
continue;
case OP_PUSH27:
pc = load_push<27>(state, pc + 1, code_end);
pc = load_push<27>(state, pc + 1);
continue;
case OP_PUSH28:
pc = load_push<28>(state, pc + 1, code_end);
pc = load_push<28>(state, pc + 1);
continue;
case OP_PUSH29:
pc = load_push<29>(state, pc + 1, code_end);
pc = load_push<29>(state, pc + 1);
continue;
case OP_PUSH30:
pc = load_push<30>(state, pc + 1, code_end);
pc = load_push<30>(state, pc + 1);
continue;
case OP_PUSH31:
pc = load_push<31>(state, pc + 1, code_end);
pc = load_push<31>(state, pc + 1);
continue;
case OP_PUSH32:
pc = load_push<32>(state, pc + 1, code_end);
pc = load_push<32>(state, pc + 1);
continue;

case OP_DUP1:
Expand Down
4 changes: 3 additions & 1 deletion lib/evmone/baseline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "execution_state.hpp"
#include <evmc/evmc.h>
#include <evmc/utils.h>
#include <memory>
#include <vector>

namespace evmone
Expand All @@ -18,7 +19,8 @@ struct CodeAnalysis
{
using JumpdestMap = std::vector<bool>;

JumpdestMap jumpdest_map;
const std::unique_ptr<uint8_t[]> padded_code;
const JumpdestMap jumpdest_map;
};

/// Analyze the code to build the bitmap of valid JUMPDEST locations.
Expand Down
4 changes: 2 additions & 2 deletions test/bench/helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ inline void analyse(benchmark::State& state, evmc_revision rev, bytes_view code)
for (auto _ : state)
{
auto r = evmone::analyze(rev, code.data(), code.size());
benchmark::DoNotOptimize(r);
benchmark::DoNotOptimize(&r);
bytes_analysed += code.size();
}

Expand All @@ -38,7 +38,7 @@ inline void baseline_analyze(benchmark::State& state, bytes_view code) noexcept
for (auto _ : state)
{
auto r = evmone::baseline::analyze(code.data(), code.size());
benchmark::DoNotOptimize(r);
benchmark::DoNotOptimize(&r);
chfast marked this conversation as resolved.
Show resolved Hide resolved
bytes_analysed += code.size();
}

Expand Down