Skip to content

Commit

Permalink
Merge pull request #315 from ethereum/baseline_padded_code
Browse files Browse the repository at this point in the history
Optimize Baseline interpreter by using padded code
  • Loading branch information
chfast authored Jun 4, 2021
2 parents 3a2dbeb + 6538e6a commit 9182e3d
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 46 deletions.
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).
// 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);
bytes_analysed += code.size();
}

Expand Down

0 comments on commit 9182e3d

Please sign in to comment.