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

Switch execution to evmone baseline interpreter #619

Merged
merged 8 commits into from
Mar 25, 2022
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
2 changes: 1 addition & 1 deletion cmd/check_changes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ int main(int argc, char* argv[]) {
throw std::runtime_error("Unable to retrieve chain config");
}

AnalysisCache analysis_cache;
AdvancedAnalysisCache analysis_cache;
ObjectPool<EvmoneExecutionState> state_pool;
std::vector<Receipt> receipts;
auto engine{consensus::engine_factory(chain_config.value())};
Expand Down
2 changes: 1 addition & 1 deletion cmd/scan_txs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ int main(int argc, char* argv[]) {
// running on the same machine.
// std::unique_ptr<lmdb::Transaction> txn{env->begin_ro_transaction()};

AnalysisCache analysis_cache;
AdvancedAnalysisCache analysis_cache;
ObjectPool<EvmoneExecutionState> state_pool;
std::vector<Receipt> receipts;

Expand Down
7 changes: 2 additions & 5 deletions core/silkworm/chain/config.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2021 The Silkworm Authors
Copyright 2021-2022 The Silkworm Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,7 +32,6 @@ static const std::vector<std::pair<std::string, const ChainConfig*>> kKnownChain
constexpr const char* kTerminalTotalDifficulty{"terminalTotalDifficulty"};
constexpr const char* kTerminalBlockNumber{"terminalBlockNumber"};
constexpr const char* kTerminalBlockHash{"terminalBlockHash"};
constexpr const char* kMergeForkBlock{"mergeForkBlock"};

static inline void member_to_json(nlohmann::json& json, const std::string& key, const std::optional<uint64_t>& source) {
if (source.has_value()) {
Expand Down Expand Up @@ -75,7 +74,6 @@ nlohmann::json ChainConfig::to_json() const noexcept {
member_to_json(ret, "muirGlacierBlock", muir_glacier_block);
member_to_json(ret, "arrowGlacierBlock", arrow_glacier_block);
member_to_json(ret, kTerminalBlockNumber, terminal_block_number);
member_to_json(ret, kMergeForkBlock, merge_fork_block);

if (terminal_total_difficulty.has_value()) {
// TODO (Andrew) geth probably treats terminalTotalDifficulty as a JSON number
Expand Down Expand Up @@ -114,8 +112,7 @@ std::optional<ChainConfig> ChainConfig::from_json(const nlohmann::json& json) no
read_json_config_member(json, "muirGlacierBlock", config.muir_glacier_block);
read_json_config_member(json, "arrowGlacierBlock", config.arrow_glacier_block);
read_json_config_member(json, kTerminalBlockNumber, config.terminal_block_number);
read_json_config_member(json, kMergeForkBlock, config.merge_fork_block);


if (json.contains(kTerminalTotalDifficulty)) {
config.terminal_total_difficulty =
intx::from_string<intx::uint256>(json[kTerminalTotalDifficulty].get<std::string>());
Expand Down
8 changes: 5 additions & 3 deletions core/silkworm/chain/config.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020-2021 The Silkworm Authors
Copyright 2020-2022 The Silkworm Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -51,7 +51,9 @@ struct ChainConfig {
"berlinBlock", // EVMC_BERLIN
"londonBlock", // EVMC_LONDON
// there's no evmc_revision for arrowGlacierBlock
"shanghaiBlock", // EVMC_SHANGHAI
"mergeForkBlock", // EVMC_PARIS, corresponds to FORK_NEXT_VALUE of EIP-3675
"shanghaiBlock", // EVMC_SHANGHAI
"cancunBlock", // EVMC_CANCUN
};

static_assert(std::size(kJsonForkNames) == EVMC_MAX_REVISION);
Expand All @@ -77,7 +79,7 @@ struct ChainConfig {
std::optional<intx::uint256> terminal_total_difficulty{std::nullopt};
std::optional<uint64_t> terminal_block_number{std::nullopt};
std::optional<evmc::bytes32> terminal_block_hash{std::nullopt};
std::optional<uint64_t> merge_fork_block{std::nullopt};

// Returns the revision level at given block number
// In other words, on behalf of Json chain config data
// returns whether specific HF have occurred
Expand Down
4 changes: 2 additions & 2 deletions core/silkworm/chain/config_test.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2021 The Silkworm Authors
Copyright 2021-2022 The Silkworm Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -159,7 +159,7 @@ TEST_CASE("JSON serialization") {
CHECK(config2->terminal_total_difficulty == intx::from_string<intx::uint256>("39387012740608862000000"));
CHECK(config2->terminal_block_number == 10000);
CHECK(config2->terminal_block_hash == 0x6dc57fd586f41ee340124c3a005642af7731a9ca7a7b70d989a7e2833e4ab740_bytes32);
CHECK(config2->merge_fork_block == 10000);
CHECK(config2->revision_block(EVMC_PARIS) == 10000);

CHECK(config2->to_json() == merge_test_json);
}
Expand Down
5 changes: 1 addition & 4 deletions core/silkworm/common/base.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020-2021 The Silkworm Authors
Copyright 2020-2022 The Silkworm Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -24,10 +24,7 @@
#include <string>
#include <string_view>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include <evmc/evmc.hpp>
#pragma GCC diagnostic pop

namespace silkworm {

Expand Down
8 changes: 5 additions & 3 deletions core/silkworm/execution/analysis_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

namespace silkworm {

std::shared_ptr<EvmoneCodeAnalysis> AnalysisCache::get(const evmc::bytes32& key, evmc_revision revision) noexcept {
std::shared_ptr<evmone::advanced::AdvancedCodeAnalysis> AdvancedAnalysisCache::get(const evmc::bytes32& key,
evmc_revision revision) noexcept {
if (revision_ == revision) {
const auto* ptr{cache_.get(key)};
return ptr ? *ptr : nullptr;
Expand All @@ -29,8 +30,9 @@ std::shared_ptr<EvmoneCodeAnalysis> AnalysisCache::get(const evmc::bytes32& key,
}
}

void AnalysisCache::put(const evmc::bytes32& key, const std::shared_ptr<EvmoneCodeAnalysis>& analysis,
evmc_revision revision) noexcept {
void AdvancedAnalysisCache::put(const evmc::bytes32& key,
const std::shared_ptr<evmone::advanced::AdvancedCodeAnalysis>& analysis,
evmc_revision revision) noexcept {
if (revision_ != revision) {
// multiple revisions are not supported
cache_.clear();
Expand Down
31 changes: 16 additions & 15 deletions core/silkworm/execution/analysis_cache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,45 +19,46 @@

#include <memory>

#include <evmone/advanced_analysis.hpp>
#include <evmone/baseline.hpp>

#include <silkworm/common/base.hpp>
#include <silkworm/common/lru_cache.hpp>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include <evmone/advanced_analysis.hpp>
#pragma GCC diagnostic pop

namespace silkworm {

using EvmoneCodeAnalysis = evmone::advanced::AdvancedCodeAnalysis;
// Cache of EVM baseline analyses.
using BaselineAnalysisCache = lru_cache<evmc::bytes32, std::shared_ptr<evmone::baseline::CodeAnalysis>>;

/** @brief Cache of EVM analyses.
/** @brief Cache of EVM advanced analyses.
*
* Analyses performed for different EVM revisions do not coexist in the cache
* Adavanced interpreter analyses performed for different EVM revisions do not coexist in the cache
* and all other revisions are evicted on revision update.
*/
class AnalysisCache {
class AdvancedAnalysisCache {
public:
static constexpr size_t kDefaultMaxSize{5'000};

explicit AnalysisCache(size_t maxSize = kDefaultMaxSize) : cache_{maxSize} {}
explicit AdvancedAnalysisCache(size_t maxSize = kDefaultMaxSize) : cache_{maxSize} {}

AnalysisCache(const AnalysisCache&) = delete;
AnalysisCache& operator=(const AnalysisCache&) = delete;
// Not copyable nor movable
AdvancedAnalysisCache(const AdvancedAnalysisCache&) = delete;
AdvancedAnalysisCache& operator=(const AdvancedAnalysisCache&) = delete;

/** @brief Gets an EVM analysis from the cache.
* A nullptr is returned if there's nothing in the cache for this key & revision.
*/
std::shared_ptr<EvmoneCodeAnalysis> get(const evmc::bytes32& key, evmc_revision revision) noexcept;
std::shared_ptr<evmone::advanced::AdvancedCodeAnalysis> get(const evmc::bytes32& key,
evmc_revision revision) noexcept;

/** @brief Puts an EVM analysis into the cache.
* All cache entries for other EVM revisions are evicted.
*/
void put(const evmc::bytes32& key, const std::shared_ptr<EvmoneCodeAnalysis>& analysis,
void put(const evmc::bytes32& key, const std::shared_ptr<evmone::advanced::AdvancedCodeAnalysis>& analysis,
evmc_revision revision) noexcept;

private:
lru_cache<evmc::bytes32, std::shared_ptr<EvmoneCodeAnalysis>> cache_;
lru_cache<evmc::bytes32, std::shared_ptr<evmone::advanced::AdvancedCodeAnalysis>> cache_;
evmc_revision revision_{EVMC_MAX_REVISION};
};

Expand Down
44 changes: 28 additions & 16 deletions core/silkworm/execution/evm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

#include <ethash/keccak.hpp>
#include <evmone/advanced_execution.hpp>
#include <evmone/baseline.hpp>
#include <evmone/evmone.h>
#include <evmone/tracing.hpp>
#include <evmone/vm.hpp>
Expand Down Expand Up @@ -156,7 +155,7 @@ evmc::result EVM::create(const evmc_message& message) noexcept {
message.value, // value
};

res = execute(deploy_message, ByteView{message.input_data, message.input_size}, /*code_hash=*/std::nullopt);
res = execute(deploy_message, ByteView{message.input_data, message.input_size}, /*code_hash=*/nullptr);

if (res.status_code == EVMC_SUCCESS) {
const size_t code_len{res.output_size};
Expand Down Expand Up @@ -240,8 +239,7 @@ evmc::result EVM::call(const evmc_message& message) noexcept {
}

const evmc::bytes32 code_hash{state_.get_code_hash(message.code_address)};

res = execute(message, code, code_hash);
res = execute(message, code, &code_hash);
}

if (res.status_code != EVMC_SUCCESS) {
Expand All @@ -254,18 +252,18 @@ evmc::result EVM::call(const evmc_message& message) noexcept {
return res;
}

evmc::result EVM::execute(const evmc_message& msg, ByteView code, std::optional<evmc::bytes32> code_hash) noexcept {
evmc::result EVM::execute(const evmc_message& msg, ByteView code, const evmc::bytes32* code_hash) noexcept {
const evmc_revision rev{revision()};

evmc_result res;
if (exo_evm) {
EvmHost host{*this};
res = exo_evm->execute(exo_evm, &host.get_interface(), host.to_context(), rev, &msg, code.data(), code.size());
} else if (code_hash != std::nullopt && advanced_analysis_cache != nullptr) {
res = execute_with_default_interpreter(rev, msg, code, *code_hash);
} else if (code_hash && advanced_analysis_cache) {
res = execute_with_advanced_interpreter(rev, msg, code, *code_hash);
} else {
// for one-off execution baseline interpreter is generally faster
res = execute_with_baseline_interpreter(rev, msg, code);
res = execute_with_baseline_interpreter(rev, msg, code, code_hash);
}

return evmc::result{res};
Expand All @@ -290,28 +288,42 @@ void EVM::release_state(gsl::owner<EvmoneExecutionState*> state) noexcept {
}
}

evmc_result EVM::execute_with_baseline_interpreter(evmc_revision rev, const evmc_message& msg, ByteView code) noexcept {
const auto vm{static_cast<evmone::VM*>(evm1_)};
const auto analysis{evmone::baseline::analyze(code)};
evmc_result EVM::execute_with_baseline_interpreter(evmc_revision rev, const evmc_message& msg, ByteView code,
const evmc::bytes32* code_hash) noexcept {
std::shared_ptr<evmone::baseline::CodeAnalysis> analysis;
const bool use_cache{code_hash && baseline_analysis_cache};
if (use_cache) {
const auto* ptr{baseline_analysis_cache->get(*code_hash)};
if (ptr) {
analysis = *ptr;
}
}
if (!analysis) {
analysis = std::make_shared<evmone::baseline::CodeAnalysis>(evmone::baseline::analyze(code));
if (use_cache) {
baseline_analysis_cache->put(*code_hash, analysis);
}
}

EvmHost host{*this};
gsl::owner<EvmoneExecutionState*> state{acquire_state()};
state->reset(msg, rev, host.get_interface(), host.to_context(), code);

evmc_result res{evmone::baseline::execute(*vm, *state, analysis)};
const auto vm{static_cast<evmone::VM*>(evm1_)};
evmc_result res{evmone::baseline::execute(*vm, *state, *analysis)};

release_state(state);

return res;
}

evmc_result EVM::execute_with_default_interpreter(evmc_revision rev, const evmc_message& msg, ByteView code,
const evmc::bytes32& code_hash) noexcept {
evmc_result EVM::execute_with_advanced_interpreter(evmc_revision rev, const evmc_message& msg, ByteView code,
const evmc::bytes32& code_hash) noexcept {
assert(advanced_analysis_cache != nullptr);

std::shared_ptr<EvmoneCodeAnalysis> analysis{advanced_analysis_cache->get(code_hash, rev)};
std::shared_ptr<evmone::advanced::AdvancedCodeAnalysis> analysis{advanced_analysis_cache->get(code_hash, rev)};
if (!analysis) {
analysis = std::make_shared<EvmoneCodeAnalysis>(evmone::advanced::analyze(rev, code));
analysis = std::make_shared<evmone::advanced::AdvancedCodeAnalysis>(evmone::advanced::analyze(rev, code));
advanced_analysis_cache->put(code_hash, analysis, rev);
}

Expand Down
20 changes: 9 additions & 11 deletions core/silkworm/execution/evm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@
#include <silkworm/state/intra_block_state.hpp>
#include <silkworm/types/block.hpp>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include <evmone/advanced_analysis.hpp>
#pragma GCC diagnostic pop

namespace silkworm {

struct CallResult {
Expand Down Expand Up @@ -78,8 +73,11 @@ class EVM {

void add_tracer(EvmTracer& tracer) noexcept;

// Use for better performance with evmone baseline interpreter
BaselineAnalysisCache* baseline_analysis_cache{nullptr};

// Point to a cache instance in order to enable execution with evmone advanced rather than baseline interpreter
AnalysisCache* advanced_analysis_cache{nullptr};
AdvancedAnalysisCache* advanced_analysis_cache{nullptr};

ObjectPool<EvmoneExecutionState>* state_pool{nullptr}; // use for better performance

Expand All @@ -94,13 +92,13 @@ class EVM {

evmc::result call(const evmc_message& message) noexcept;

evmc::result execute(const evmc_message& message, ByteView code, std::optional<evmc::bytes32> code_hash) noexcept;
evmc::result execute(const evmc_message& message, ByteView code, const evmc::bytes32* code_hash) noexcept;

evmc_result execute_with_baseline_interpreter(evmc_revision rev, const evmc_message& message,
ByteView code) noexcept;
evmc_result execute_with_baseline_interpreter(evmc_revision rev, const evmc_message& message, ByteView code,
const evmc::bytes32* code_hash) noexcept;

evmc_result execute_with_default_interpreter(evmc_revision rev, const evmc_message& message, ByteView code,
const evmc::bytes32& code_hash) noexcept;
evmc_result execute_with_advanced_interpreter(evmc_revision rev, const evmc_message& message, ByteView code,
const evmc::bytes32& code_hash) noexcept;

gsl::owner<EvmoneExecutionState*> acquire_state() noexcept;
void release_state(gsl::owner<EvmoneExecutionState*> state) noexcept;
Expand Down
2 changes: 1 addition & 1 deletion core/silkworm/execution/evm_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ TEST_CASE("Maximum call depth") {

EVM evm{block, state, kMainnetConfig};

AnalysisCache analysis_cache{/*maxSize=*/16};
AdvancedAnalysisCache analysis_cache{/*maxSize=*/16};
evm.advanced_analysis_cache = &analysis_cache;

Transaction txn{};
Expand Down
7 changes: 4 additions & 3 deletions node/silkworm/stagedsync/stage_execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ StageResult Execution::forward(db::RWTxn& txn) {
prune_receipts = std::min(prune_receipts, hashstate_stage_progress - 1);
}

AnalysisCache analysis_cache;
static constexpr size_t kCacheSize{5'000};
BaselineAnalysisCache analysis_cache{kCacheSize};
ObjectPool<EvmoneExecutionState> state_pool;

while (!is_stopping() && block_num_ <= max_block_num) {
Expand Down Expand Up @@ -156,7 +157,7 @@ std::queue<Block> Execution::prefetch_blocks(db::RWTxn& txn, BlockNum from, Bloc
return ret;
}

StageResult Execution::execute_batch(db::RWTxn& txn, BlockNum max_block_num, AnalysisCache& analysis_cache,
StageResult Execution::execute_batch(db::RWTxn& txn, BlockNum max_block_num, BaselineAnalysisCache& analysis_cache,
ObjectPool<EvmoneExecutionState>& state_pool, BlockNum prune_history_threshold,
BlockNum prune_receipts_threshold) {
try {
Expand Down Expand Up @@ -195,7 +196,7 @@ StageResult Execution::execute_batch(db::RWTxn& txn, BlockNum max_block_num, Ana
}

ExecutionProcessor processor(block, *consensus_engine_, buffer, node_settings_->chain_config.value());
processor.evm().advanced_analysis_cache = &analysis_cache;
processor.evm().baseline_analysis_cache = &analysis_cache;
processor.evm().state_pool = &state_pool;

// TODO(Andrea) Add Tracer
Expand Down
2 changes: 1 addition & 1 deletion node/silkworm/stagedsync/stage_execution.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Execution final : public IStage {

//! \brief Executes a batch of blocks
//! \remarks A batch completes when either max block is reached or buffer dimensions overflow
StageResult execute_batch(db::RWTxn& txn, BlockNum max_block_num, AnalysisCache& analysis_cache,
StageResult execute_batch(db::RWTxn& txn, BlockNum max_block_num, BaselineAnalysisCache& analysis_cache,
ObjectPool<EvmoneExecutionState>& state_pool, BlockNum prune_history_threshold,
BlockNum prune_receipts_threshold);

Expand Down