Skip to content

Commit ae53cbb

Browse files
authored
Merge pull request #144 from ethereum/block_info
Block info optimization
2 parents c26ae95 + dd738dc commit ae53cbb

File tree

5 files changed

+54
-45
lines changed

5 files changed

+54
-45
lines changed

lib/evmone/analysis.cpp

+44-25
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,21 @@
88

99
namespace evmone
1010
{
11+
struct block_analysis
12+
{
13+
int gas_cost = 0;
14+
15+
int stack_req = 0;
16+
int stack_max_growth = 0;
17+
int stack_change = 0;
18+
19+
/// The index of the beginblock instruction that starts the block.
20+
/// This is the place where the analysis data is going to be dumped.
21+
size_t begin_block_index = 0;
22+
23+
explicit block_analysis(size_t index) noexcept : begin_block_index{index} {}
24+
};
25+
1126
inline constexpr uint64_t load64be(const unsigned char* data) noexcept
1227
{
1328
return uint64_t{data[7]} | (uint64_t{data[6]} << 8) | (uint64_t{data[5]} << 16) |
@@ -31,13 +46,9 @@ code_analysis analyze(evmc_revision rev, const uint8_t* code, size_t code_size)
3146

3247
const auto* instr_table = evmc_get_instruction_metrics_table(rev);
3348

34-
// Create new block.
35-
auto block = &analysis.blocks.emplace_back();
36-
int block_stack_change = 0;
37-
{
38-
auto& beginblock_instr = analysis.instrs.emplace_back(opx_beginblock_fn);
39-
beginblock_instr.arg.number = static_cast<int>(analysis.blocks.size() - 1);
40-
}
49+
// Create first block.
50+
analysis.instrs.emplace_back(opx_beginblock_fn);
51+
auto block = block_analysis{0};
4152

4253
const auto code_end = code + code_size;
4354
auto code_pos = code;
@@ -50,19 +61,12 @@ code_analysis analyze(evmc_revision rev, const uint8_t* code, size_t code_size)
5061
const auto instr_stack_req = metrics.num_stack_arguments;
5162
const auto instr_stack_change = metrics.num_stack_returned_items - instr_stack_req;
5263

53-
// TODO: Define a block_analysis struct with regular ints for analysis.
54-
// Compress it when block is closed.
55-
auto stack_req = instr_stack_req - block_stack_change;
56-
if (stack_req > std::numeric_limits<decltype(block->stack_req)>::max())
57-
stack_req = std::numeric_limits<decltype(block->stack_req)>::max();
58-
59-
block->stack_req = std::max(block->stack_req, static_cast<int16_t>(stack_req));
60-
block_stack_change += instr_stack_change;
61-
block->stack_max_growth =
62-
static_cast<int16_t>(std::max(int{block->stack_max_growth}, block_stack_change));
64+
block.stack_req = std::max(block.stack_req, instr_stack_req - block.stack_change);
65+
block.stack_change += instr_stack_change;
66+
block.stack_max_growth = std::max(block.stack_max_growth, block.stack_change);
6367

6468
if (metrics.gas_cost > 0) // can be -1 for undefined instruction
65-
block->gas_cost += metrics.gas_cost;
69+
block.gas_cost += metrics.gas_cost;
6670

6771
if (opcode == OP_JUMPDEST)
6872
{
@@ -137,25 +141,40 @@ code_analysis analyze(evmc_revision rev, const uint8_t* code, size_t code_size)
137141
case OP_STATICCALL:
138142
case OP_CREATE:
139143
case OP_CREATE2:
140-
instr.arg.number = block->gas_cost;
144+
instr.arg.number = block.gas_cost;
141145
break;
142146

143147
case OP_PC:
144148
instr.arg.number = static_cast<int>(code_pos - code - 1);
145149
break;
146150
}
147151

152+
// If this is a terminating instruction or the next instruction is a JUMPDEST.
148153
if (is_terminator || (code_pos != code_end && *code_pos == OP_JUMPDEST))
149154
{
150-
// Create new basic block if
151-
// this is a terminating instruction or the next instruction is a JUMPDEST.
152-
block = &analysis.blocks.emplace_back();
153-
block_stack_change = 0;
154-
auto& beginblock_instr = analysis.instrs.emplace_back(opx_beginblock_fn);
155-
beginblock_instr.arg.number = static_cast<int>(analysis.blocks.size() - 1);
155+
// Save current block.
156+
const auto stack_req = block.stack_req <= std::numeric_limits<int16_t>::max() ?
157+
static_cast<int16_t>(block.stack_req) :
158+
std::numeric_limits<int16_t>::max();
159+
const auto stack_max_growth = static_cast<int16_t>(block.stack_max_growth);
160+
analysis.instrs[block.begin_block_index].arg.block = {
161+
block.gas_cost, stack_req, stack_max_growth};
162+
163+
164+
// Create new block.
165+
analysis.instrs.emplace_back(opx_beginblock_fn);
166+
block = block_analysis{analysis.instrs.size() - 1};
156167
}
157168
}
158169

170+
// Save current block.
171+
const auto stack_req = block.stack_req <= std::numeric_limits<int16_t>::max() ?
172+
static_cast<int16_t>(block.stack_req) :
173+
std::numeric_limits<int16_t>::max();
174+
const auto stack_max_growth = static_cast<int16_t>(block.stack_max_growth);
175+
analysis.instrs[block.begin_block_index].arg.block = {
176+
block.gas_cost, stack_req, stack_max_growth};
177+
159178
// Make sure the last block is terminated.
160179
// TODO: This is not needed if the last instruction is a terminating one.
161180
analysis.instrs.emplace_back(fns[OP_STOP]);

lib/evmone/analysis.hpp

-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,6 @@ static_assert(sizeof(block_info) == 8);
185185
struct code_analysis
186186
{
187187
std::vector<instr_info> instrs;
188-
std::vector<block_info> blocks;
189188

190189
/// Storage for large push values.
191190
std::vector<intx::uint256> push_values;

lib/evmone/instructions.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,7 @@ const instr_info* op_selfdestruct(const instr_info*, execution_state& state) noe
12021202

12031203
const instr_info* opx_beginblock(const instr_info* instr, execution_state& state) noexcept
12041204
{
1205-
auto& block = state.analysis->blocks[static_cast<size_t>(instr->arg.number)];
1205+
auto& block = instr->arg.block;
12061206

12071207
if ((state.gas_left -= block.gas_cost) < 0)
12081208
return state.exit(EVMC_OUT_OF_GAS);

test/unittests/analysis_test.cpp

+8-17
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ TEST(analysis, example1)
2121
ASSERT_EQ(analysis.instrs.size(), 8);
2222

2323
EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]);
24-
EXPECT_EQ(analysis.instrs[0].arg.number, 0);
2524
EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_PUSH1]);
2625
EXPECT_EQ(analysis.instrs[2].fn, op_table[OP_PUSH1]);
2726
EXPECT_EQ(analysis.instrs[3].fn, op_table[OP_MSTORE8]);
@@ -30,10 +29,10 @@ TEST(analysis, example1)
3029
EXPECT_EQ(analysis.instrs[6].fn, op_table[OP_SSTORE]);
3130
EXPECT_EQ(analysis.instrs[7].fn, op_table[OP_STOP]);
3231

33-
ASSERT_EQ(analysis.blocks.size(), 1);
34-
EXPECT_EQ(analysis.blocks[0].gas_cost, 14);
35-
EXPECT_EQ(analysis.blocks[0].stack_req, 0);
36-
EXPECT_EQ(analysis.blocks[0].stack_max_growth, 2);
32+
const auto& block = analysis.instrs[0].arg.block;
33+
EXPECT_EQ(block.gas_cost, 14);
34+
EXPECT_EQ(block.stack_req, 0);
35+
EXPECT_EQ(block.stack_max_growth, 2);
3736
}
3837

3938
TEST(analysis, stack_up_and_down)
@@ -43,16 +42,15 @@ TEST(analysis, stack_up_and_down)
4342

4443
ASSERT_EQ(analysis.instrs.size(), 20);
4544
EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]);
46-
EXPECT_EQ(analysis.instrs[0].arg.number, 0);
4745
EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_DUP2]);
4846
EXPECT_EQ(analysis.instrs[2].fn, op_table[OP_DUP1]);
4947
EXPECT_EQ(analysis.instrs[8].fn, op_table[OP_POP]);
5048
EXPECT_EQ(analysis.instrs[18].fn, op_table[OP_PUSH1]);
5149

52-
ASSERT_EQ(analysis.blocks.size(), 1);
53-
EXPECT_EQ(analysis.blocks[0].gas_cost, 7 * 3 + 10 * 2 + 3);
54-
EXPECT_EQ(analysis.blocks[0].stack_req, 3);
55-
EXPECT_EQ(analysis.blocks[0].stack_max_growth, 7);
50+
const auto& block = analysis.instrs[0].arg.block;
51+
EXPECT_EQ(block.gas_cost, 7 * 3 + 10 * 2 + 3);
52+
EXPECT_EQ(block.stack_req, 3);
53+
EXPECT_EQ(block.stack_max_growth, 7);
5654
}
5755

5856
TEST(analysis, push)
@@ -77,7 +75,6 @@ TEST(analysis, jumpdest_skip)
7775
const auto code = bytecode{} + OP_STOP + OP_JUMPDEST;
7876
auto analysis = evmone::analyze(rev, &code[0], code.size());
7977

80-
EXPECT_EQ(analysis.blocks.size(), 2);
8178
ASSERT_EQ(analysis.instrs.size(), 4);
8279
EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]);
8380
EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_STOP]);
@@ -90,7 +87,6 @@ TEST(analysis, jump1)
9087
const auto code = jump(add(4, 2)) + OP_JUMPDEST + mstore(0, 3) + ret(0, 0x20) + jump(6);
9188
const auto analysis = analyze(rev, &code[0], code.size());
9289

93-
ASSERT_EQ(analysis.blocks.size(), 4);
9490
ASSERT_EQ(analysis.jumpdest_offsets.size(), 1);
9591
ASSERT_EQ(analysis.jumpdest_targets.size(), 1);
9692
EXPECT_EQ(analysis.jumpdest_offsets[0], 6);
@@ -105,7 +101,6 @@ TEST(analysis, empty)
105101
bytes code;
106102
auto analysis = evmone::analyze(rev, &code[0], code.size());
107103

108-
EXPECT_EQ(analysis.blocks.size(), 1);
109104
ASSERT_EQ(analysis.instrs.size(), 2);
110105
EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]);
111106
EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_STOP]);
@@ -116,7 +111,6 @@ TEST(analysis, only_jumpdest)
116111
const auto code = bytecode{OP_JUMPDEST};
117112
auto analysis = evmone::analyze(rev, &code[0], code.size());
118113

119-
ASSERT_EQ(analysis.blocks.size(), 1);
120114
ASSERT_EQ(analysis.jumpdest_offsets.size(), 1);
121115
ASSERT_EQ(analysis.jumpdest_targets.size(), 1);
122116
EXPECT_EQ(analysis.jumpdest_offsets[0], 0);
@@ -128,7 +122,6 @@ TEST(analysis, jumpi_at_the_end)
128122
const auto code = bytecode{OP_JUMPI};
129123
auto analysis = evmone::analyze(rev, &code[0], code.size());
130124

131-
EXPECT_EQ(analysis.blocks.size(), 2);
132125
ASSERT_EQ(analysis.instrs.size(), 4);
133126
EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]);
134127
EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_JUMPI]);
@@ -143,7 +136,6 @@ TEST(analysis, terminated_last_block)
143136
const auto code = ret(0, 0);
144137
auto analysis = evmone::analyze(rev, &code[0], code.size());
145138

146-
EXPECT_EQ(analysis.blocks.size(), 2);
147139
ASSERT_EQ(analysis.instrs.size(), 6);
148140
EXPECT_EQ(analysis.instrs[0].fn, op_table[OPX_BEGINBLOCK]);
149141
EXPECT_EQ(analysis.instrs[3].fn, op_table[OP_RETURN]);
@@ -156,7 +148,6 @@ TEST(analysis, jumpdests_groups)
156148
const auto code = 3 * OP_JUMPDEST + push(1) + 3 * OP_JUMPDEST + push(2) + OP_JUMPI;
157149
auto analysis = evmone::analyze(rev, &code[0], code.size());
158150

159-
EXPECT_EQ(analysis.blocks.size(), 7);
160151
ASSERT_EQ(analysis.instrs.size(), 11);
161152
EXPECT_EQ(analysis.instrs[0].fn, op_table[OP_JUMPDEST]);
162153
EXPECT_EQ(analysis.instrs[1].fn, op_table[OP_JUMPDEST]);

test/utils/dump.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ void dump(const evmone::code_analysis& analysis)
2828

2929
if (c == OPX_BEGINBLOCK)
3030
{
31-
block = &analysis.blocks[size_t(instr.arg.number)];
31+
block = &instr.arg.block;
3232

3333
const auto get_jumpdest_offset = [&analysis](size_t index) noexcept
3434
{

0 commit comments

Comments
 (0)