Skip to content

Commit e7832a1

Browse files
iovoidjrchatrucmpaulucci
authored
refactor(levm): refactor execution into being non-recursive (#2473)
**Motivation** In #2445 a Stack Overflow was found with high call stacks. Turns out each level of recursion was adding ~4kB to the stack. Simply reducing the stack usage would've required extensive stack usage and hard to understand code. **Description** Makes code execution non-recursive, and instead uses call_stacks to save the call stacks and return_data to save return parameters. Functions that took the current frame by parameter now read it with a function. Closes #2445 --------- Co-authored-by: Javier Rodríguez Chatruc <49622509+jrchatruc@users.noreply.github.com> Co-authored-by: Martin Paulucci <martin.c.paulucci@gmail.com>
1 parent 0d08a2f commit e7832a1

File tree

15 files changed

+929
-774
lines changed

15 files changed

+929
-774
lines changed

cmd/ef_tests/blockchain/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ clean-vectors: ## 🗑️ Clean test vectors
2424
rm -f $(SPECTEST_ARTIFACT)
2525

2626
test-levm: $(SPECTEST_VECTORS_DIR) ## 🧪 Run blockchain tests with LEVM
27-
RUST_MIN_STACK=11000000 cargo test --release --features levm
27+
cargo test --release --features levm
2828

2929
test-revm: $(SPECTEST_VECTORS_DIR) ## 🧪 Run blockchain tests with REVM
3030
cargo test --release

crates/vm/levm/src/call_frame.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ impl CallFrame {
130130
}
131131
}
132132

133-
pub fn next_opcode(&mut self) -> Opcode {
133+
pub fn next_opcode(&self) -> Opcode {
134134
match self.bytecode.get(self.pc).copied().map(Opcode::from) {
135135
Some(opcode) => opcode,
136136
None => Opcode::STOP,

crates/vm/levm/src/execution_handlers.rs

Lines changed: 106 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,22 @@ impl<'a> VM<'a> {
1616
pub fn handle_precompile_result(
1717
&mut self,
1818
precompile_result: Result<Bytes, VMError>,
19-
current_call_frame: &mut CallFrame,
2019
backup: StateBackup,
20+
current_call_frame: &mut CallFrame,
2121
) -> Result<ExecutionReport, VMError> {
2222
match precompile_result {
23-
Ok(output) => {
24-
self.call_frames.push(current_call_frame.clone());
25-
26-
Ok(ExecutionReport {
27-
result: TxResult::Success,
28-
gas_used: current_call_frame.gas_used,
29-
gas_refunded: self.env.refunded_gas,
30-
output,
31-
logs: std::mem::take(&mut current_call_frame.logs),
32-
})
33-
}
23+
Ok(output) => Ok(ExecutionReport {
24+
result: TxResult::Success,
25+
gas_used: current_call_frame.gas_used,
26+
gas_refunded: self.env.refunded_gas,
27+
output,
28+
logs: std::mem::take(&mut current_call_frame.logs),
29+
}),
3430
Err(error) => {
3531
if error.is_internal() {
3632
return Err(error);
3733
}
3834

39-
self.call_frames.push(current_call_frame.clone());
40-
4135
self.restore_state(backup);
4236

4337
Ok(ExecutionReport {
@@ -50,111 +44,107 @@ impl<'a> VM<'a> {
5044
}
5145
}
5246
}
53-
pub fn handle_current_opcode(
54-
&mut self,
55-
opcode: Opcode,
56-
current_call_frame: &mut CallFrame,
57-
) -> Result<OpcodeResult, VMError> {
47+
pub fn handle_current_opcode(&mut self, opcode: Opcode) -> Result<OpcodeResult, VMError> {
5848
match opcode {
5949
Opcode::STOP => Ok(OpcodeResult::Halt),
60-
Opcode::ADD => self.op_add(current_call_frame),
61-
Opcode::MUL => self.op_mul(current_call_frame),
62-
Opcode::SUB => self.op_sub(current_call_frame),
63-
Opcode::DIV => self.op_div(current_call_frame),
64-
Opcode::SDIV => self.op_sdiv(current_call_frame),
65-
Opcode::MOD => self.op_mod(current_call_frame),
66-
Opcode::SMOD => self.op_smod(current_call_frame),
67-
Opcode::ADDMOD => self.op_addmod(current_call_frame),
68-
Opcode::MULMOD => self.op_mulmod(current_call_frame),
69-
Opcode::EXP => self.op_exp(current_call_frame),
70-
Opcode::SIGNEXTEND => self.op_signextend(current_call_frame),
71-
Opcode::LT => self.op_lt(current_call_frame),
72-
Opcode::GT => self.op_gt(current_call_frame),
73-
Opcode::SLT => self.op_slt(current_call_frame),
74-
Opcode::SGT => self.op_sgt(current_call_frame),
75-
Opcode::EQ => self.op_eq(current_call_frame),
76-
Opcode::ISZERO => self.op_iszero(current_call_frame),
77-
Opcode::KECCAK256 => self.op_keccak256(current_call_frame),
78-
Opcode::CALLDATALOAD => self.op_calldataload(current_call_frame),
79-
Opcode::CALLDATASIZE => self.op_calldatasize(current_call_frame),
80-
Opcode::CALLDATACOPY => self.op_calldatacopy(current_call_frame),
81-
Opcode::RETURNDATASIZE => self.op_returndatasize(current_call_frame),
82-
Opcode::RETURNDATACOPY => self.op_returndatacopy(current_call_frame),
83-
Opcode::JUMP => self.op_jump(current_call_frame),
84-
Opcode::JUMPI => self.op_jumpi(current_call_frame),
85-
Opcode::JUMPDEST => self.op_jumpdest(current_call_frame),
86-
Opcode::PC => self.op_pc(current_call_frame),
87-
Opcode::BLOCKHASH => self.op_blockhash(current_call_frame),
88-
Opcode::COINBASE => self.op_coinbase(current_call_frame),
89-
Opcode::TIMESTAMP => self.op_timestamp(current_call_frame),
90-
Opcode::NUMBER => self.op_number(current_call_frame),
91-
Opcode::PREVRANDAO => self.op_prevrandao(current_call_frame),
92-
Opcode::GASLIMIT => self.op_gaslimit(current_call_frame),
93-
Opcode::CHAINID => self.op_chainid(current_call_frame),
94-
Opcode::BASEFEE => self.op_basefee(current_call_frame),
95-
Opcode::BLOBHASH => self.op_blobhash(current_call_frame),
96-
Opcode::BLOBBASEFEE => self.op_blobbasefee(current_call_frame),
97-
Opcode::PUSH0 => self.op_push0(current_call_frame),
50+
Opcode::ADD => self.op_add(),
51+
Opcode::MUL => self.op_mul(),
52+
Opcode::SUB => self.op_sub(),
53+
Opcode::DIV => self.op_div(),
54+
Opcode::SDIV => self.op_sdiv(),
55+
Opcode::MOD => self.op_mod(),
56+
Opcode::SMOD => self.op_smod(),
57+
Opcode::ADDMOD => self.op_addmod(),
58+
Opcode::MULMOD => self.op_mulmod(),
59+
Opcode::EXP => self.op_exp(),
60+
Opcode::SIGNEXTEND => self.op_signextend(),
61+
Opcode::LT => self.op_lt(),
62+
Opcode::GT => self.op_gt(),
63+
Opcode::SLT => self.op_slt(),
64+
Opcode::SGT => self.op_sgt(),
65+
Opcode::EQ => self.op_eq(),
66+
Opcode::ISZERO => self.op_iszero(),
67+
Opcode::KECCAK256 => self.op_keccak256(),
68+
Opcode::CALLDATALOAD => self.op_calldataload(),
69+
Opcode::CALLDATASIZE => self.op_calldatasize(),
70+
Opcode::CALLDATACOPY => self.op_calldatacopy(),
71+
Opcode::RETURNDATASIZE => self.op_returndatasize(),
72+
Opcode::RETURNDATACOPY => self.op_returndatacopy(),
73+
Opcode::JUMP => self.op_jump(),
74+
Opcode::JUMPI => self.op_jumpi(),
75+
Opcode::JUMPDEST => self.op_jumpdest(),
76+
Opcode::PC => self.op_pc(),
77+
Opcode::BLOCKHASH => self.op_blockhash(),
78+
Opcode::COINBASE => self.op_coinbase(),
79+
Opcode::TIMESTAMP => self.op_timestamp(),
80+
Opcode::NUMBER => self.op_number(),
81+
Opcode::PREVRANDAO => self.op_prevrandao(),
82+
Opcode::GASLIMIT => self.op_gaslimit(),
83+
Opcode::CHAINID => self.op_chainid(),
84+
Opcode::BASEFEE => self.op_basefee(),
85+
Opcode::BLOBHASH => self.op_blobhash(),
86+
Opcode::BLOBBASEFEE => self.op_blobbasefee(),
87+
Opcode::PUSH0 => self.op_push0(),
9888
// PUSHn
9989
op if (Opcode::PUSH1..=Opcode::PUSH32).contains(&op) => {
10090
let n_bytes = get_n_value(op, Opcode::PUSH1)?;
101-
self.op_push(current_call_frame, n_bytes)
91+
self.op_push(n_bytes)
10292
}
103-
Opcode::AND => self.op_and(current_call_frame),
104-
Opcode::OR => self.op_or(current_call_frame),
105-
Opcode::XOR => self.op_xor(current_call_frame),
106-
Opcode::NOT => self.op_not(current_call_frame),
107-
Opcode::BYTE => self.op_byte(current_call_frame),
108-
Opcode::SHL => self.op_shl(current_call_frame),
109-
Opcode::SHR => self.op_shr(current_call_frame),
110-
Opcode::SAR => self.op_sar(current_call_frame),
93+
Opcode::AND => self.op_and(),
94+
Opcode::OR => self.op_or(),
95+
Opcode::XOR => self.op_xor(),
96+
Opcode::NOT => self.op_not(),
97+
Opcode::BYTE => self.op_byte(),
98+
Opcode::SHL => self.op_shl(),
99+
Opcode::SHR => self.op_shr(),
100+
Opcode::SAR => self.op_sar(),
111101
// DUPn
112102
op if (Opcode::DUP1..=Opcode::DUP16).contains(&op) => {
113103
let depth = get_n_value(op, Opcode::DUP1)?;
114-
self.op_dup(current_call_frame, depth)
104+
self.op_dup(depth)
115105
}
116106
// SWAPn
117107
op if (Opcode::SWAP1..=Opcode::SWAP16).contains(&op) => {
118108
let depth = get_n_value(op, Opcode::SWAP1)?;
119-
self.op_swap(current_call_frame, depth)
109+
self.op_swap(depth)
120110
}
121-
Opcode::POP => self.op_pop(current_call_frame),
111+
Opcode::POP => self.op_pop(),
122112
op if (Opcode::LOG0..=Opcode::LOG4).contains(&op) => {
123113
let number_of_topics = get_number_of_topics(op)?;
124-
self.op_log(current_call_frame, number_of_topics)
114+
self.op_log(number_of_topics)
125115
}
126-
Opcode::MLOAD => self.op_mload(current_call_frame),
127-
Opcode::MSTORE => self.op_mstore(current_call_frame),
128-
Opcode::MSTORE8 => self.op_mstore8(current_call_frame),
129-
Opcode::SLOAD => self.op_sload(current_call_frame),
130-
Opcode::SSTORE => self.op_sstore(current_call_frame),
131-
Opcode::MSIZE => self.op_msize(current_call_frame),
132-
Opcode::GAS => self.op_gas(current_call_frame),
133-
Opcode::MCOPY => self.op_mcopy(current_call_frame),
134-
Opcode::CALL => self.op_call(current_call_frame),
135-
Opcode::CALLCODE => self.op_callcode(current_call_frame),
136-
Opcode::RETURN => self.op_return(current_call_frame),
137-
Opcode::DELEGATECALL => self.op_delegatecall(current_call_frame),
138-
Opcode::STATICCALL => self.op_staticcall(current_call_frame),
139-
Opcode::CREATE => self.op_create(current_call_frame),
140-
Opcode::CREATE2 => self.op_create2(current_call_frame),
141-
Opcode::TLOAD => self.op_tload(current_call_frame),
142-
Opcode::TSTORE => self.op_tstore(current_call_frame),
143-
Opcode::SELFBALANCE => self.op_selfbalance(current_call_frame),
144-
Opcode::ADDRESS => self.op_address(current_call_frame),
145-
Opcode::ORIGIN => self.op_origin(current_call_frame),
146-
Opcode::BALANCE => self.op_balance(current_call_frame),
147-
Opcode::CALLER => self.op_caller(current_call_frame),
148-
Opcode::CALLVALUE => self.op_callvalue(current_call_frame),
149-
Opcode::CODECOPY => self.op_codecopy(current_call_frame),
150-
Opcode::CODESIZE => self.op_codesize(current_call_frame),
151-
Opcode::GASPRICE => self.op_gasprice(current_call_frame),
152-
Opcode::EXTCODESIZE => self.op_extcodesize(current_call_frame),
153-
Opcode::EXTCODECOPY => self.op_extcodecopy(current_call_frame),
154-
Opcode::EXTCODEHASH => self.op_extcodehash(current_call_frame),
155-
Opcode::REVERT => self.op_revert(current_call_frame),
116+
Opcode::MLOAD => self.op_mload(),
117+
Opcode::MSTORE => self.op_mstore(),
118+
Opcode::MSTORE8 => self.op_mstore8(),
119+
Opcode::SLOAD => self.op_sload(),
120+
Opcode::SSTORE => self.op_sstore(),
121+
Opcode::MSIZE => self.op_msize(),
122+
Opcode::GAS => self.op_gas(),
123+
Opcode::MCOPY => self.op_mcopy(),
124+
Opcode::CALL => self.op_call(),
125+
Opcode::CALLCODE => self.op_callcode(),
126+
Opcode::RETURN => self.op_return(),
127+
Opcode::DELEGATECALL => self.op_delegatecall(),
128+
Opcode::STATICCALL => self.op_staticcall(),
129+
Opcode::CREATE => self.op_create(),
130+
Opcode::CREATE2 => self.op_create2(),
131+
Opcode::TLOAD => self.op_tload(),
132+
Opcode::TSTORE => self.op_tstore(),
133+
Opcode::SELFBALANCE => self.op_selfbalance(),
134+
Opcode::ADDRESS => self.op_address(),
135+
Opcode::ORIGIN => self.op_origin(),
136+
Opcode::BALANCE => self.op_balance(),
137+
Opcode::CALLER => self.op_caller(),
138+
Opcode::CALLVALUE => self.op_callvalue(),
139+
Opcode::CODECOPY => self.op_codecopy(),
140+
Opcode::CODESIZE => self.op_codesize(),
141+
Opcode::GASPRICE => self.op_gasprice(),
142+
Opcode::EXTCODESIZE => self.op_extcodesize(),
143+
Opcode::EXTCODECOPY => self.op_extcodecopy(),
144+
Opcode::EXTCODEHASH => self.op_extcodehash(),
145+
Opcode::REVERT => self.op_revert(),
156146
Opcode::INVALID => self.op_invalid(),
157-
Opcode::SELFDESTRUCT => self.op_selfdestruct(current_call_frame),
147+
Opcode::SELFDESTRUCT => self.op_selfdestruct(),
158148

159149
_ => Err(VMError::OpcodeNotFound),
160150
}
@@ -163,9 +153,11 @@ impl<'a> VM<'a> {
163153
pub fn handle_opcode_result(
164154
&mut self,
165155
current_call_frame: &mut CallFrame,
166-
backup: StateBackup,
167156
) -> Result<ExecutionReport, VMError> {
168-
self.call_frames.push(current_call_frame.clone());
157+
let backup = self
158+
.backups
159+
.pop()
160+
.ok_or(VMError::Internal(InternalError::CouldNotPopCallframe))?;
169161
// On successful create check output validity
170162
if (self.is_create() && current_call_frame.depth == 0)
171163
|| current_call_frame.create_op_called
@@ -236,10 +228,11 @@ impl<'a> VM<'a> {
236228
&mut self,
237229
error: VMError,
238230
current_call_frame: &mut CallFrame,
239-
backup: StateBackup,
240231
) -> Result<ExecutionReport, VMError> {
241-
self.call_frames.push(current_call_frame.clone());
242-
232+
let backup = self
233+
.backups
234+
.pop()
235+
.ok_or(VMError::Internal(InternalError::CouldNotPopCallframe))?;
243236
if error.is_internal() {
244237
return Err(error);
245238
}
@@ -252,13 +245,17 @@ impl<'a> VM<'a> {
252245
current_call_frame.gas_used = current_call_frame.gas_used.saturating_add(left_gas);
253246
}
254247

248+
let refunded = backup.refunded_gas;
249+
let output = std::mem::take(&mut current_call_frame.output); // Bytes::new() if error is not RevertOpcode
250+
let gas_used = current_call_frame.gas_used;
251+
255252
self.restore_state(backup);
256253

257254
Ok(ExecutionReport {
258255
result: TxResult::Revert(error),
259-
gas_used: current_call_frame.gas_used,
260-
gas_refunded: self.env.refunded_gas,
261-
output: std::mem::take(&mut current_call_frame.output), // Bytes::new() if error is not RevertOpcode
256+
gas_used,
257+
gas_refunded: refunded,
258+
output,
262259
logs: vec![],
263260
})
264261
}

0 commit comments

Comments
 (0)