From 8c6c3cdc90bb402bb2d717fac147a4575614b023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 4 May 2021 15:33:29 +0200 Subject: [PATCH 1/4] baseline: Copy and pad the code before execution --- lib/evmone/baseline.cpp | 16 ++++++++++++++-- lib/evmone/baseline.hpp | 4 +++- test/bench/helpers.hpp | 4 ++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp index 71b1865a49..cc45ceb01e 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline.cpp @@ -19,15 +19,27 @@ CodeAnalysis analyze(const uint8_t* code, size_t code_size) 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) + size_t i = 0; + while (i < code_size) { 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; + ++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 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 diff --git a/lib/evmone/baseline.hpp b/lib/evmone/baseline.hpp index fabc867860..fcc0475fb6 100644 --- a/lib/evmone/baseline.hpp +++ b/lib/evmone/baseline.hpp @@ -6,6 +6,7 @@ #include "execution_state.hpp" #include #include +#include #include namespace evmone @@ -18,7 +19,8 @@ struct CodeAnalysis { using JumpdestMap = std::vector; - JumpdestMap jumpdest_map; + const std::unique_ptr padded_code; + const JumpdestMap jumpdest_map; }; /// Analyze the code to build the bitmap of valid JUMPDEST locations. diff --git a/test/bench/helpers.hpp b/test/bench/helpers.hpp index cc5676b586..bd330a207d 100644 --- a/test/bench/helpers.hpp +++ b/test/bench/helpers.hpp @@ -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(); } @@ -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(); } From dd4370190c187dcae34e3097e92168ad043e3bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 4 May 2021 21:06:50 +0200 Subject: [PATCH 2/4] baseline: Use padded code for execution --- lib/evmone/baseline.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp index cc45ceb01e..a072f0070f 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline.cpp @@ -111,6 +111,9 @@ inline evmc_status_code check_requirements(const char* const* instruction_names, template 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); @@ -124,7 +127,11 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana while (pc != code_end) { if constexpr (TracingEnabled) - tracer->notify_instruction_start(static_cast(pc - code), state); + { + const auto offset = static_cast(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); From 5d7cade8bb486685e9d6197272ccdbd7ff2ea455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 31 Mar 2021 22:06:29 +0200 Subject: [PATCH 3/4] baseline: Use "infinite" interpreter loop Guaranteed to terminate because padded code ends with STOP. --- lib/evmone/baseline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp index a072f0070f..b9025e1fec 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline.cpp @@ -124,7 +124,7 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana 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) { From 6538e6a97b7a394883db9e484f36b26a97a033f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 31 Mar 2021 22:09:33 +0200 Subject: [PATCH 4/4] baseline: Do not check for out-of-buffer push load This cannot overflow code buffer because code ends with valid STOP instruction. --- lib/evmone/baseline.cpp | 73 +++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp index b9025e1fec..070cfafc43 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline.cpp @@ -58,14 +58,10 @@ const uint8_t* op_jump( } template -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(buffer)); return code + Len; @@ -122,7 +118,6 @@ 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 (true) // Guaranteed to terminate because padded code ends with STOP. { @@ -448,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: