Skip to content

Commit c08c338

Browse files
committed
Attach block metadata to JUMPDEST and JUMPI instead of OPX_BEGINBLOCK
1 parent 7846cb0 commit c08c338

File tree

3 files changed

+75
-47
lines changed

3 files changed

+75
-47
lines changed

lib/evmone/advanced_analysis.cpp

+38-27
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ AdvancedCodeAnalysis analyze(evmc_revision rev, bytes_view code) noexcept
4646

4747
AdvancedCodeAnalysis analysis;
4848

49-
const auto max_instrs_size = code.size() + 1;
49+
const auto max_instrs_size = code.size() + 2; // Additional OPX_BEGINBLOCK and STOP
5050
analysis.instrs.reserve(max_instrs_size);
5151

5252
// This is 2x more than needed but using (code.size() / 2 + 1) increases page-faults 1000x.
@@ -67,38 +67,60 @@ AdvancedCodeAnalysis analyze(evmc_revision rev, bytes_view code) noexcept
6767
const auto opcode = *code_pos++;
6868
const auto& opcode_info = op_tbl[opcode];
6969

70-
block.stack_req = std::max(block.stack_req, opcode_info.stack_req - block.stack_change);
71-
block.stack_change += opcode_info.stack_change;
72-
block.stack_max_growth = std::max(block.stack_max_growth, block.stack_change);
73-
74-
block.gas_cost += opcode_info.gas_cost;
75-
7670
if (opcode == OP_JUMPDEST)
7771
{
72+
// Save current block.
73+
analysis.instrs[block.begin_block_index].arg.block = block.close();
74+
// Create new block.
75+
block = BlockAnalysis{analysis.instrs.size()};
76+
7877
// The JUMPDEST is always the first instruction in the block.
79-
// We don't have to insert anything to the instruction table.
8078
analysis.jumpdest_offsets.emplace_back(static_cast<int32_t>(code_pos - code_begin - 1));
81-
analysis.jumpdest_targets.emplace_back(
82-
static_cast<int32_t>(analysis.instrs.size() - 1));
79+
analysis.jumpdest_targets.emplace_back(static_cast<int32_t>(analysis.instrs.size()));
8380
}
84-
else
85-
analysis.instrs.emplace_back(opcode_info.fn);
81+
82+
analysis.instrs.emplace_back(opcode_info.fn);
83+
84+
block.stack_req = std::max(block.stack_req, opcode_info.stack_req - block.stack_change);
85+
block.stack_change += opcode_info.stack_change;
86+
block.stack_max_growth = std::max(block.stack_max_growth, block.stack_change);
87+
88+
block.gas_cost += opcode_info.gas_cost;
8689

8790
auto& instr = analysis.instrs.back();
8891

89-
bool is_terminator = false; // A flag whenever this is a block terminating instruction.
9092
switch (opcode)
9193
{
9294
default:
9395
break;
9496

9597
case OP_JUMP:
96-
case OP_JUMPI:
9798
case OP_STOP:
9899
case OP_RETURN:
99100
case OP_REVERT:
100101
case OP_SELFDESTRUCT:
101-
is_terminator = true;
102+
// Skip dead block instructions till next JUMPDEST or code end.
103+
// Current instruction will be final one in the block.
104+
while (code_pos != code_end && *code_pos != OP_JUMPDEST)
105+
{
106+
if (*code_pos >= OP_PUSH1 && *code_pos <= OP_PUSH32)
107+
{
108+
const auto push_size = static_cast<size_t>(*code_pos - OP_PUSH1) + 1;
109+
code_pos = std::min(code_pos + push_size + 1, code_end);
110+
}
111+
else
112+
++code_pos;
113+
}
114+
break;
115+
116+
case OP_JUMPI:
117+
// JUMPI will be final instruction in the current block
118+
// and hold metadata for the next block.
119+
120+
// Save current block.
121+
analysis.instrs[block.begin_block_index].arg.block = block.close();
122+
// Create new block.
123+
block = BlockAnalysis{analysis.instrs.size() - 1};
102124
break;
103125

104126
case ANY_SMALL_PUSH:
@@ -157,17 +179,6 @@ AdvancedCodeAnalysis analyze(evmc_revision rev, bytes_view code) noexcept
157179
instr.arg.number = code_pos - code_begin - 1;
158180
break;
159181
}
160-
161-
// If this is a terminating instruction or the next instruction is a JUMPDEST.
162-
if (is_terminator || (code_pos != code_end && *code_pos == OP_JUMPDEST))
163-
{
164-
// Save current block.
165-
analysis.instrs[block.begin_block_index].arg.block = block.close();
166-
167-
// Create new block.
168-
analysis.instrs.emplace_back(opx_beginblock_fn);
169-
block = BlockAnalysis{analysis.instrs.size() - 1};
170-
}
171182
}
172183

173184
// Save current block.
@@ -177,7 +188,7 @@ AdvancedCodeAnalysis analyze(evmc_revision rev, bytes_view code) noexcept
177188
// TODO: This is not needed if the last instruction is a terminating one.
178189
analysis.instrs.emplace_back(op_tbl[OP_STOP].fn);
179190

180-
// FIXME: assert(analysis.instrs.size() <= max_instrs_size);
191+
assert(analysis.instrs.size() <= max_instrs_size);
181192

182193
// Make sure the push_values has not been reallocated. Otherwise iterators are invalid.
183194
assert(analysis.push_values.size() <= max_args_storage_size);

lib/evmone/advanced_instructions.cpp

+20-1
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,32 @@ const Instruction* op_jump(const Instruction*, AdvancedExecutionState& state) no
138138
return &state.analysis.advanced->instrs[static_cast<size_t>(pc)];
139139
}
140140

141+
const Instruction* opx_beginblock(const Instruction* instr, AdvancedExecutionState& state) noexcept
142+
{
143+
auto& block = instr->arg.block;
144+
145+
if ((state.gas_left -= block.gas_cost) < 0)
146+
return state.exit(EVMC_OUT_OF_GAS);
147+
148+
if (static_cast<int>(state.stack.size()) < block.stack_req)
149+
return state.exit(EVMC_STACK_UNDERFLOW);
150+
151+
if (static_cast<int>(state.stack.size()) + block.stack_max_growth > StackSpace::limit)
152+
return state.exit(EVMC_STACK_OVERFLOW);
153+
154+
state.current_block_cost = block.gas_cost;
155+
return ++instr;
156+
}
157+
141158
const Instruction* op_jumpi(const Instruction* instr, AdvancedExecutionState& state) noexcept
142159
{
143160
if (state.stack[1] != 0)
144161
instr = op_jump(instr, state);
145162
else
146163
{
147164
state.stack.pop();
148-
++instr;
165+
166+
instr = opx_beginblock(instr, state);
149167
}
150168

151169
// OPT: The pc must be the BEGINBLOCK (even in fallback case),
@@ -234,6 +252,7 @@ constexpr std::array<instruction_exec_fn, 256> instruction_implementations = [](
234252
table[OP_PC] = op_pc;
235253
table[OP_GAS] = op_gas;
236254
table[OPX_BEGINBLOCK] = opx_beginblock;
255+
table[OP_JUMPDEST] = opx_beginblock;
237256

238257
for (auto op = size_t{OP_PUSH1}; op <= OP_PUSH8; ++op)
239258
table[op] = op_push_small;

test/unittests/analysis_test.cpp

+17-19
Original file line numberDiff line numberDiff line change
@@ -115,19 +115,18 @@ TEST(analysis, only_jumpdest)
115115
ASSERT_EQ(analysis.jumpdest_offsets.size(), 1);
116116
ASSERT_EQ(analysis.jumpdest_targets.size(), 1);
117117
EXPECT_EQ(analysis.jumpdest_offsets[0], 0);
118-
EXPECT_EQ(analysis.jumpdest_targets[0], 0);
118+
EXPECT_EQ(analysis.jumpdest_targets[0], 1);
119119
}
120120

121121
TEST(analysis, jumpi_at_the_end)
122122
{
123123
const auto code = bytecode{OP_JUMPI};
124124
auto analysis = analyze(rev, code);
125125

126-
ASSERT_EQ(analysis.instrs.size(), 4);
126+
ASSERT_EQ(analysis.instrs.size(), 3);
127127
EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OPX_BEGINBLOCK].fn);
128128
EXPECT_EQ(analysis.instrs[1].fn, op_tbl[OP_JUMPI].fn);
129-
EXPECT_EQ(analysis.instrs[2].fn, op_tbl[OPX_BEGINBLOCK].fn);
130-
EXPECT_EQ(analysis.instrs[3].fn, op_tbl[OP_STOP].fn);
129+
EXPECT_EQ(analysis.instrs[2].fn, op_tbl[OP_STOP].fn);
131130
}
132131

133132
TEST(analysis, terminated_last_block)
@@ -137,11 +136,10 @@ TEST(analysis, terminated_last_block)
137136
const auto code = ret(0, 0);
138137
auto analysis = analyze(rev, code);
139138

140-
ASSERT_EQ(analysis.instrs.size(), 6);
139+
ASSERT_EQ(analysis.instrs.size(), 5);
141140
EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OPX_BEGINBLOCK].fn);
142141
EXPECT_EQ(analysis.instrs[3].fn, op_tbl[OP_RETURN].fn);
143-
EXPECT_EQ(analysis.instrs[4].fn, op_tbl[OPX_BEGINBLOCK].fn);
144-
EXPECT_EQ(analysis.instrs[5].fn, op_tbl[OP_STOP].fn);
142+
EXPECT_EQ(analysis.instrs[4].fn, op_tbl[OP_STOP].fn);
145143
}
146144

147145
TEST(analysis, jumpdests_groups)
@@ -150,33 +148,33 @@ TEST(analysis, jumpdests_groups)
150148
auto analysis = analyze(rev, code);
151149

152150
ASSERT_EQ(analysis.instrs.size(), 11);
153-
EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OP_JUMPDEST].fn);
151+
EXPECT_EQ(analysis.instrs[0].fn, op_tbl[OPX_BEGINBLOCK].fn);
154152
EXPECT_EQ(analysis.instrs[1].fn, op_tbl[OP_JUMPDEST].fn);
155153
EXPECT_EQ(analysis.instrs[2].fn, op_tbl[OP_JUMPDEST].fn);
156-
EXPECT_EQ(analysis.instrs[3].fn, op_tbl[OP_PUSH1].fn);
157-
EXPECT_EQ(analysis.instrs[4].fn, op_tbl[OP_JUMPDEST].fn);
154+
EXPECT_EQ(analysis.instrs[3].fn, op_tbl[OP_JUMPDEST].fn);
155+
EXPECT_EQ(analysis.instrs[4].fn, op_tbl[OP_PUSH1].fn);
158156
EXPECT_EQ(analysis.instrs[5].fn, op_tbl[OP_JUMPDEST].fn);
159157
EXPECT_EQ(analysis.instrs[6].fn, op_tbl[OP_JUMPDEST].fn);
160-
EXPECT_EQ(analysis.instrs[7].fn, op_tbl[OP_PUSH1].fn);
161-
EXPECT_EQ(analysis.instrs[8].fn, op_tbl[OP_JUMPI].fn);
162-
EXPECT_EQ(analysis.instrs[9].fn, op_tbl[OPX_BEGINBLOCK].fn);
158+
EXPECT_EQ(analysis.instrs[7].fn, op_tbl[OP_JUMPDEST].fn);
159+
EXPECT_EQ(analysis.instrs[8].fn, op_tbl[OP_PUSH1].fn);
160+
EXPECT_EQ(analysis.instrs[9].fn, op_tbl[OP_JUMPI].fn);
163161
EXPECT_EQ(analysis.instrs[10].fn, op_tbl[OP_STOP].fn);
164162

165163

166164
ASSERT_EQ(analysis.jumpdest_offsets.size(), 6);
167165
ASSERT_EQ(analysis.jumpdest_targets.size(), 6);
168166
EXPECT_EQ(analysis.jumpdest_offsets[0], 0);
169-
EXPECT_EQ(analysis.jumpdest_targets[0], 0);
167+
EXPECT_EQ(analysis.jumpdest_targets[0], 1);
170168
EXPECT_EQ(analysis.jumpdest_offsets[1], 1);
171-
EXPECT_EQ(analysis.jumpdest_targets[1], 1);
169+
EXPECT_EQ(analysis.jumpdest_targets[1], 2);
172170
EXPECT_EQ(analysis.jumpdest_offsets[2], 2);
173-
EXPECT_EQ(analysis.jumpdest_targets[2], 2);
171+
EXPECT_EQ(analysis.jumpdest_targets[2], 3);
174172
EXPECT_EQ(analysis.jumpdest_offsets[3], 5);
175-
EXPECT_EQ(analysis.jumpdest_targets[3], 4);
173+
EXPECT_EQ(analysis.jumpdest_targets[3], 5);
176174
EXPECT_EQ(analysis.jumpdest_offsets[4], 6);
177-
EXPECT_EQ(analysis.jumpdest_targets[4], 5);
175+
EXPECT_EQ(analysis.jumpdest_targets[4], 6);
178176
EXPECT_EQ(analysis.jumpdest_offsets[5], 7);
179-
EXPECT_EQ(analysis.jumpdest_targets[5], 6);
177+
EXPECT_EQ(analysis.jumpdest_targets[5], 7);
180178
}
181179

182180
TEST(analysis, example1_eof1)

0 commit comments

Comments
 (0)