-
Notifications
You must be signed in to change notification settings - Fork 306
/
Copy pathhelpers.hpp
162 lines (130 loc) · 5.3 KB
/
helpers.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2019 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "test/utils/utils.hpp"
#include <benchmark/benchmark.h>
#include <evmc/evmc.hpp>
#include <evmc/mocked_host.hpp>
#include <evmone/advanced_analysis.hpp>
#include <evmone/advanced_execution.hpp>
#include <evmone/baseline.hpp>
#include <evmone/eof.hpp>
#include <evmone/vm.hpp>
namespace evmone::test
{
extern std::map<std::string_view, evmc::VM> registered_vms;
constexpr auto default_revision = EVMC_ISTANBUL;
constexpr auto default_gas_limit = std::numeric_limits<int64_t>::max();
template <typename ExecutionStateT, typename AnalysisT>
using ExecuteFn = evmc::Result(evmc::VM& vm, ExecutionStateT& exec_state, const AnalysisT&,
const evmc_message&, evmc_revision, evmc::Host&, bytes_view);
template <typename AnalysisT>
using AnalyseFn = AnalysisT(evmc_revision, bytes_view);
struct FakeExecutionState
{};
struct FakeCodeAnalysis
{};
inline advanced::AdvancedCodeAnalysis advanced_analyse(evmc_revision rev, bytes_view code)
{
return advanced::analyze(rev, code);
}
inline baseline::CodeAnalysis baseline_analyse(evmc_revision rev, bytes_view code)
{
return baseline::analyze(rev, code);
}
inline FakeCodeAnalysis evmc_analyse(evmc_revision /*rev*/, bytes_view /*code*/)
{
return {};
}
inline evmc::Result advanced_execute(evmc::VM& /*vm*/, advanced::AdvancedExecutionState& exec_state,
const advanced::AdvancedCodeAnalysis& analysis, const evmc_message& msg, evmc_revision rev,
evmc::Host& host, bytes_view code)
{
exec_state.reset(msg, rev, host.get_interface(), host.to_context(), code);
return evmc::Result{execute(exec_state, analysis)};
}
inline evmc::Result baseline_execute(evmc::VM& c_vm, ExecutionState& exec_state,
const baseline::CodeAnalysis& analysis, const evmc_message& msg, evmc_revision rev,
evmc::Host& host, bytes_view code)
{
const auto& vm = *static_cast<evmone::VM*>(c_vm.get_raw_pointer());
exec_state.reset(msg, rev, host.get_interface(), host.to_context(), code);
return evmc::Result{baseline::execute(vm, exec_state, analysis)};
}
inline evmc::Result evmc_execute(evmc::VM& vm, FakeExecutionState& /*exec_state*/,
const FakeCodeAnalysis& /*analysis*/, const evmc_message& msg, evmc_revision rev,
evmc::Host& host, bytes_view code) noexcept
{
return vm.execute(host, rev, msg, code.data(), code.size());
}
template <typename AnalysisT, AnalyseFn<AnalysisT> analyse_fn>
inline void bench_analyse(benchmark::State& state, evmc_revision rev, bytes_view code) noexcept
{
auto bytes_analysed = uint64_t{0};
for (auto _ : state)
{
auto r = analyse_fn(rev, code);
benchmark::DoNotOptimize(&r);
bytes_analysed += code.size();
}
using benchmark::Counter;
state.counters["size"] = Counter(static_cast<double>(code.size()));
state.counters["rate"] = Counter(static_cast<double>(bytes_analysed), Counter::kIsRate);
}
template <typename ExecutionStateT, typename AnalysisT,
ExecuteFn<ExecutionStateT, AnalysisT> execute_fn, AnalyseFn<AnalysisT> analyse_fn>
inline void bench_execute(benchmark::State& state, evmc::VM& vm, bytes_view code, bytes_view input,
bytes_view expected_output) noexcept
{
constexpr auto rev = default_revision;
constexpr auto gas_limit = default_gas_limit;
const auto analysis = analyse_fn(rev, code);
evmc::MockedHost host;
ExecutionStateT exec_state;
evmc_message msg{};
msg.kind = EVMC_CALL;
msg.gas = gas_limit;
msg.input_data = input.data();
msg.input_size = input.size();
{ // Test run.
const auto r = execute_fn(vm, exec_state, analysis, msg, rev, host, code);
if (r.status_code != EVMC_SUCCESS)
{
state.SkipWithError(("failure: " + std::to_string(r.status_code)).c_str());
return;
}
if (!expected_output.empty())
{
const auto output = bytes_view{r.output_data, r.output_size};
if (output != expected_output)
{
state.SkipWithError(
("got: " + hex(output) + " expected: " + hex(expected_output)).c_str());
return;
}
}
}
auto total_gas_used = int64_t{0};
auto iteration_gas_used = int64_t{0};
for (auto _ : state)
{
const auto r = execute_fn(vm, exec_state, analysis, msg, rev, host, code);
iteration_gas_used = gas_limit - r.gas_left;
total_gas_used += iteration_gas_used;
}
using benchmark::Counter;
state.counters["gas_used"] = Counter(static_cast<double>(iteration_gas_used));
state.counters["gas_rate"] = Counter(static_cast<double>(total_gas_used), Counter::kIsRate);
}
constexpr auto bench_advanced_execute = bench_execute<advanced::AdvancedExecutionState,
advanced::AdvancedCodeAnalysis, advanced_execute, advanced_analyse>;
constexpr auto bench_baseline_execute =
bench_execute<ExecutionState, baseline::CodeAnalysis, baseline_execute, baseline_analyse>;
inline void bench_evmc_execute(benchmark::State& state, evmc::VM& vm, bytes_view code,
bytes_view input = {}, bytes_view expected_output = {})
{
bench_execute<FakeExecutionState, FakeCodeAnalysis, evmc_execute, evmc_analyse>(
state, vm, code, input, expected_output);
}
} // namespace evmone::test