Skip to content

Commit c84c429

Browse files
authored
Merge pull request #341 from ethereum/baseline_instruction_table
baseline: Use custom instruction table
2 parents d81a8fd + f8900a2 commit c84c429

4 files changed

+83
-8
lines changed

lib/evmone/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ add_library(evmone
1313
analysis.hpp
1414
baseline.cpp
1515
baseline.hpp
16+
baseline_instruction_table.cpp
17+
baseline_instruction_table.hpp
1618
execution.cpp
1719
execution.hpp
1820
instruction_traits.hpp

lib/evmone/baseline.cpp

+8-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// SPDX-License-Identifier: Apache-2.0
44

55
#include "baseline.hpp"
6+
#include "baseline_instruction_table.hpp"
67
#include "execution_state.hpp"
78
#include "instructions.hpp"
89
#include "vm.hpp"
@@ -84,12 +85,12 @@ inline void op_return(ExecutionState& state) noexcept
8485
state.status = StatusCode;
8586
}
8687

87-
inline evmc_status_code check_requirements(const char* const* instruction_names,
88-
const evmc_instruction_metrics* instruction_metrics, ExecutionState& state, uint8_t op) noexcept
88+
inline evmc_status_code check_requirements(
89+
const InstructionTable& instruction_table, ExecutionState& state, uint8_t op) noexcept
8990
{
90-
const auto metrics = instruction_metrics[op];
91+
const auto metrics = instruction_table[op];
9192

92-
if (instruction_names[op] == nullptr)
93+
if (metrics.gas_cost == instr::undefined)
9394
return EVMC_UNDEFINED_INSTRUCTION;
9495

9596
if ((state.gas_left -= metrics.gas_cost) < 0)
@@ -98,7 +99,7 @@ inline evmc_status_code check_requirements(const char* const* instruction_names,
9899
const auto stack_size = state.stack.size();
99100
if (stack_size < metrics.stack_height_required)
100101
return EVMC_STACK_UNDERFLOW;
101-
if (stack_size + metrics.stack_height_change > Stack::limit)
102+
if (stack_size == Stack::limit && metrics.can_overflow_stack)
102103
return EVMC_STACK_OVERFLOW;
103104

104105
return EVMC_SUCCESS;
@@ -114,8 +115,7 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana
114115
if constexpr (TracingEnabled)
115116
tracer->notify_execution_start(state.rev, *state.msg, state.code);
116117

117-
const auto instruction_names = evmc_get_instruction_names_table(state.rev);
118-
const auto instruction_metrics = evmc_get_instruction_metrics_table(state.rev);
118+
const auto& instruction_table = get_baseline_instruction_table(state.rev);
119119

120120
const auto* const code = state.code.data();
121121
auto pc = code;
@@ -129,7 +129,7 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana
129129
}
130130

131131
const auto op = *pc;
132-
const auto status = check_requirements(instruction_names, instruction_metrics, state, op);
132+
const auto status = check_requirements(instruction_table, state, op);
133133
if (status != EVMC_SUCCESS)
134134
{
135135
state.status = status;
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// evmone: Fast Ethereum Virtual Machine implementation
2+
// Copyright 2020 The evmone Authors.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include "baseline_instruction_table.hpp"
6+
#include "instruction_traits.hpp"
7+
#include <cassert>
8+
9+
namespace evmone
10+
{
11+
namespace
12+
{
13+
template <evmc_revision Rev>
14+
constexpr InstructionTable create_instruction_table() noexcept
15+
{
16+
InstructionTable table{};
17+
for (size_t i = 0; i < table.size(); ++i)
18+
{
19+
auto& t = table[i];
20+
const auto gas_cost = instr::gas_costs<Rev>[i];
21+
t.gas_cost = gas_cost; // Include instr::undefined in the table.
22+
t.stack_height_required = instr::traits[i].stack_height_required;
23+
24+
// Because any instruction can increase stack height at most of 1,
25+
// stack overflow can only happen if stack height is already at the limit.
26+
assert(instr::traits[i].stack_height_change <= 1);
27+
t.can_overflow_stack = instr::traits[i].stack_height_change > 0;
28+
}
29+
return table;
30+
}
31+
32+
constexpr InstructionTable instruction_tables[] = {
33+
create_instruction_table<EVMC_FRONTIER>(),
34+
create_instruction_table<EVMC_HOMESTEAD>(),
35+
create_instruction_table<EVMC_TANGERINE_WHISTLE>(),
36+
create_instruction_table<EVMC_SPURIOUS_DRAGON>(),
37+
create_instruction_table<EVMC_BYZANTIUM>(),
38+
create_instruction_table<EVMC_CONSTANTINOPLE>(),
39+
create_instruction_table<EVMC_PETERSBURG>(),
40+
create_instruction_table<EVMC_ISTANBUL>(),
41+
create_instruction_table<EVMC_BERLIN>(),
42+
create_instruction_table<EVMC_LONDON>(),
43+
};
44+
static_assert(std::size(instruction_tables) == EVMC_MAX_REVISION + 1,
45+
"instruction table entry missing for an EVMC revision");
46+
} // namespace
47+
48+
const InstructionTable& get_baseline_instruction_table(evmc_revision rev) noexcept
49+
{
50+
return instruction_tables[rev];
51+
}
52+
} // namespace evmone
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// evmone: Fast Ethereum Virtual Machine implementation
2+
// Copyright 2020 The evmone Authors.
3+
// SPDX-License-Identifier: Apache-2.0
4+
#pragma once
5+
6+
#include <evmc/evmc.h>
7+
#include <array>
8+
9+
namespace evmone
10+
{
11+
struct InstructionTableEntry
12+
{
13+
int16_t gas_cost;
14+
int8_t stack_height_required;
15+
bool can_overflow_stack;
16+
};
17+
18+
using InstructionTable = std::array<InstructionTableEntry, 256>;
19+
20+
const InstructionTable& get_baseline_instruction_table(evmc_revision rev) noexcept;
21+
} // namespace evmone

0 commit comments

Comments
 (0)