Skip to content

Conversation

@iovoid
Copy link
Contributor

@iovoid iovoid commented Nov 27, 2025

Motivation

Currently we check the validity of the swap for the purpose of returning EVM errors, but swap itself contains bounds checks too.

Description

Uses unsafe code to perform the swap.

@iovoid iovoid requested a review from a team as a code owner November 27, 2025 13:03
@github-actions github-actions bot added levm Lambda EVM implementation performance Block execution throughput and performance in general labels Nov 27, 2025
@github-actions
Copy link

github-actions bot commented Nov 27, 2025

Lines of code report

Total lines added: 4
Total lines removed: 0
Total lines changed: 4

Detailed view
+-----------------------------------------+-------+------+
| File                                    | Lines | Diff |
+-----------------------------------------+-------+------+
| ethrex/crates/vm/levm/src/call_frame.rs | 348   | +4   |
+-----------------------------------------+-------+------+

@github-actions
Copy link

github-actions bot commented Nov 27, 2025

Benchmark Results Comparison

No significant difference was registered for any benchmark run.

Detailed Results

Benchmark Results: BubbleSort

Command Mean [s] Min [s] Max [s] Relative
main_revm_BubbleSort 3.010 ± 0.024 2.986 3.065 1.02 ± 0.01
main_levm_BubbleSort 3.118 ± 0.013 3.094 3.139 1.05 ± 0.01
pr_revm_BubbleSort 2.958 ± 0.016 2.941 2.986 1.00
pr_levm_BubbleSort 3.074 ± 0.007 3.066 3.089 1.04 ± 0.01

Benchmark Results: ERC20Approval

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Approval 968.7 ± 4.5 961.4 976.3 1.00 ± 0.01
main_levm_ERC20Approval 1117.1 ± 36.3 1089.6 1204.3 1.16 ± 0.04
pr_revm_ERC20Approval 964.4 ± 3.8 960.6 972.1 1.00
pr_levm_ERC20Approval 1096.6 ± 10.9 1083.4 1118.9 1.14 ± 0.01

Benchmark Results: ERC20Mint

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Mint 134.8 ± 0.6 133.6 135.5 1.02 ± 0.01
main_levm_ERC20Mint 165.6 ± 4.6 163.0 178.3 1.25 ± 0.03
pr_revm_ERC20Mint 132.6 ± 0.5 132.1 133.5 1.00
pr_levm_ERC20Mint 164.6 ± 2.6 162.1 169.0 1.24 ± 0.02

Benchmark Results: ERC20Transfer

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Transfer 234.5 ± 4.4 229.3 241.1 1.03 ± 0.02
main_levm_ERC20Transfer 281.8 ± 6.0 275.5 296.7 1.23 ± 0.03
pr_revm_ERC20Transfer 228.8 ± 0.8 227.2 229.9 1.00
pr_levm_ERC20Transfer 282.5 ± 8.7 275.9 305.9 1.23 ± 0.04

Benchmark Results: Factorial

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Factorial 229.6 ± 10.1 222.3 257.8 1.00
main_levm_Factorial 269.2 ± 2.6 266.4 274.5 1.17 ± 0.05
pr_revm_Factorial 229.8 ± 4.6 224.2 241.9 1.00 ± 0.05
pr_levm_Factorial 268.9 ± 4.7 266.0 281.8 1.17 ± 0.06

Benchmark Results: FactorialRecursive

Command Mean [s] Min [s] Max [s] Relative
main_revm_FactorialRecursive 1.676 ± 0.049 1.559 1.742 1.00
main_levm_FactorialRecursive 8.457 ± 0.064 8.383 8.608 5.05 ± 0.15
pr_revm_FactorialRecursive 1.697 ± 0.044 1.659 1.793 1.01 ± 0.04
pr_levm_FactorialRecursive 8.568 ± 0.106 8.433 8.738 5.11 ± 0.16

Benchmark Results: Fibonacci

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Fibonacci 207.8 ± 0.9 206.0 209.3 1.00 ± 0.01
main_levm_Fibonacci 263.6 ± 5.2 254.6 275.3 1.27 ± 0.03
pr_revm_Fibonacci 207.0 ± 0.5 206.4 207.8 1.00
pr_levm_Fibonacci 262.2 ± 2.8 257.0 266.7 1.27 ± 0.01

Benchmark Results: FibonacciRecursive

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_FibonacciRecursive 880.6 ± 20.3 865.1 935.5 1.18 ± 0.03
main_levm_FibonacciRecursive 748.2 ± 10.6 736.7 775.3 1.00 ± 0.02
pr_revm_FibonacciRecursive 889.8 ± 7.7 882.0 908.4 1.19 ± 0.02
pr_levm_FibonacciRecursive 747.5 ± 9.9 727.7 762.3 1.00

Benchmark Results: ManyHashes

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ManyHashes 8.4 ± 0.1 8.4 8.7 1.01 ± 0.02
main_levm_ManyHashes 9.2 ± 0.1 9.0 9.3 1.10 ± 0.02
pr_revm_ManyHashes 8.4 ± 0.1 8.3 8.6 1.00
pr_levm_ManyHashes 9.0 ± 0.1 8.9 9.3 1.08 ± 0.02

Benchmark Results: MstoreBench

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_MstoreBench 273.8 ± 12.0 266.1 306.1 1.12 ± 0.05
main_levm_MstoreBench 244.5 ± 2.1 241.0 247.3 1.00
pr_revm_MstoreBench 271.1 ± 2.3 269.0 275.1 1.11 ± 0.01
pr_levm_MstoreBench 245.7 ± 2.8 242.7 251.1 1.01 ± 0.01

Benchmark Results: Push

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Push 296.3 ± 3.8 293.6 303.6 1.01 ± 0.01
main_levm_Push 302.0 ± 0.8 300.6 303.3 1.03 ± 0.00
pr_revm_Push 293.5 ± 1.1 292.5 295.9 1.00
pr_levm_Push 308.3 ± 2.1 305.6 311.5 1.05 ± 0.01

Benchmark Results: SstoreBench_no_opt

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_SstoreBench_no_opt 166.8 ± 3.4 160.8 171.1 1.84 ± 0.06
main_levm_SstoreBench_no_opt 90.6 ± 2.0 88.3 92.6 1.00
pr_revm_SstoreBench_no_opt 166.0 ± 3.0 161.2 169.3 1.83 ± 0.05
pr_levm_SstoreBench_no_opt 93.8 ± 9.6 87.9 118.0 1.03 ± 0.11

@github-actions
Copy link

github-actions bot commented Nov 27, 2025

Benchmark Block Execution Results Comparison Against Main

Command Mean [s] Min [s] Max [s] Relative
base 61.735 ± 0.267 61.141 62.183 1.02 ± 0.01
head 60.788 ± 0.368 60.314 61.477 1.00

@iovoid
Copy link
Contributor Author

iovoid commented Nov 27, 2025

Assembly code for SWAP:

Base

.section ".text.ethrex_levm::opcode_handlers::exchange::<impl ethrex_levm::vm::VM>::op_swap","ax",@progbits
	.p2align	4
.type	ethrex_levm::opcode_handlers::exchange::<impl ethrex_levm::vm::VM>::op_swap,@function
ethrex_levm::opcode_handlers::exchange::<impl ethrex_levm::vm::VM>::op_swap:
		// crates/vm/levm/src/opcode_handlers/exchange.rs:12
		pub fn op_swap<const N: usize>(&mut self) -> Result<OpcodeResult, VMError> {
	.cfi_startproc
	sub rsp, 40
	.cfi_def_cfa_offset 48
		// crates/vm/levm/src/call_frame.rs:373
		self.gas_remaining -= gas as i64;
	add qword ptr [rsi + 1048], -3
		// crates/vm/levm/src/call_frame.rs:375
		if self.gas_remaining < 0 {
	js .LBB480_4
		// crates/vm/levm/src/call_frame.rs:145
		let index = self.offset + N;
	mov rax, qword ptr [rsi + 816]
	lea rcx, [rax + 7]
		// crates/vm/levm/src/call_frame.rs:147
		if index >= self.values.len() {
	cmp rcx, 1023
	jbe .LBB480_2
		// /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/result.rs:2087
		Err(e) => Err(From::from(e)),
	mov word ptr [rdi], 2583
		// crates/vm/levm/src/opcode_handlers/exchange.rs:19
		}
	mov rax, rdi
	add rsp, 40
	.cfi_def_cfa_offset 8
	ret
.LBB480_4:
	.cfi_def_cfa_offset 48
		// /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/result.rs:2087
		Err(e) => Err(From::from(e)),
	mov word ptr [rdi], 5143
		// crates/vm/levm/src/opcode_handlers/exchange.rs:19
		}
	mov rax, rdi
	add rsp, 40
	.cfi_def_cfa_offset 8
	ret
.LBB480_2:
	.cfi_def_cfa_offset 48
		// /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/slice/mod.rs:904
		let pa = &raw mut self[a];
	cmp rax, 1024
	jae .LBB480_3
		// crates/vm/levm/src/call_frame.rs:151
		self.values.swap(self.offset, index);
	mov rdx, qword ptr [rsi + 808]
		// /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/slice/mod.rs:904
		let pa = &raw mut self[a];
	shl rax, 5
		// /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/ptr/mod.rs:547
		unsafe { crate::intrinsics::copy_nonoverlapping(src, dst, count) }
	vmovups ymm0, ymmword ptr [rdx + rax]
	vmovups ymmword ptr [rsp], ymm0
		// /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/slice/mod.rs:905
		let pb = &raw mut self[b];
	shl rcx, 5
		// /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/ptr/mod.rs:638
		crate::intrinsics::copy(src, dst, count)
	vmovups ymm0, ymmword ptr [rdx + rcx]
	vmovups ymmword ptr [rdx + rax], ymm0
		// /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/ptr/mod.rs:547
		unsafe { crate::intrinsics::copy_nonoverlapping(src, dst, count) }
	vmovups ymm0, ymmword ptr [rsp]
	vmovups ymmword ptr [rdx + rcx], ymm0
		// crates/vm/levm/src/opcode_handlers/exchange.rs:18
		Ok(OpcodeResult::Continue)
	mov word ptr [rdi], 25
		// crates/vm/levm/src/opcode_handlers/exchange.rs:19
		}
	mov rax, rdi
	add rsp, 40
	.cfi_def_cfa_offset 8
	vzeroupper
	ret
.LBB480_3:
	.cfi_def_cfa_offset 48
		// /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/slice/mod.rs:904
		let pa = &raw mut self[a];
	lea rdx, [rip + .Lanon.6faccdc95a04ea1765da90a999bd6d7e.248]
	mov esi, 1024
	mov rdi, rax
	call qword ptr [rip + core::panicking::panic_bounds_check@GOTPCREL]

PR

.section ".text.ethrex_levm::opcode_handlers::exchange::<impl ethrex_levm::vm::VM>::op_swap","ax",@progbits
	.p2align	4
.type	ethrex_levm::opcode_handlers::exchange::<impl ethrex_levm::vm::VM>::op_swap,@function
ethrex_levm::opcode_handlers::exchange::<impl ethrex_levm::vm::VM>::op_swap:
		// crates/vm/levm/src/opcode_handlers/exchange.rs:12
		pub fn op_swap<const N: usize>(&mut self) -> Result<OpcodeResult, VMError> {
	.cfi_startproc
	mov rax, rdi
		// crates/vm/levm/src/call_frame.rs:382
		self.gas_remaining -= gas as i64;
	add qword ptr [rsi + 1048], -3
		// crates/vm/levm/src/call_frame.rs:384
		if self.gas_remaining < 0 {
	js .LBB480_5
		// crates/vm/levm/src/call_frame.rs:145
		let index = self.offset + N;
	mov rdx, qword ptr [rsi + 816]
	lea rcx, [rdx + 7]
		// crates/vm/levm/src/call_frame.rs:147
		if index >= self.values.len() {
	cmp rcx, 1023
	jbe .LBB480_2
		// ~/.rustup/toolchains/1.90.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:2087
		Err(e) => Err(From::from(e)),
	mov word ptr [rax], 2583
		// crates/vm/levm/src/opcode_handlers/exchange.rs:19
		}
	ret
.LBB480_5:
		// ~/.rustup/toolchains/1.90.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:2087
		Err(e) => Err(From::from(e)),
	mov word ptr [rax], 5143
		// crates/vm/levm/src/opcode_handlers/exchange.rs:19
		}
	ret
.LBB480_2:
		// crates/vm/levm/src/call_frame.rs:156
		let base = self.values.as_mut_ptr();
	mov rsi, qword ptr [rsi + 808]
		// ~/.rustup/toolchains/1.90.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mut_ptr.rs:1008
		unsafe { intrinsics::offset(self, count) }
	shl rdx, 5
		// ~/.rustup/toolchains/1.90.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:547
		unsafe { crate::intrinsics::copy_nonoverlapping(src, dst, count) }
	vmovups ymm0, ymmword ptr [rsi + rdx]
	vmovups ymmword ptr [rsp - 40], ymm0
		// ~/.rustup/toolchains/1.90.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mut_ptr.rs:1008
		unsafe { intrinsics::offset(self, count) }
	shl rcx, 5
		// ~/.rustup/toolchains/1.90.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:638
		crate::intrinsics::copy(src, dst, count)
	vmovups ymm0, ymmword ptr [rsi + rcx]
	vmovups ymmword ptr [rsi + rdx], ymm0
		// ~/.rustup/toolchains/1.90.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:547
		unsafe { crate::intrinsics::copy_nonoverlapping(src, dst, count) }
	vmovups ymm0, ymmword ptr [rsp - 40]
	vmovups ymmword ptr [rsi + rcx], ymm0
		// crates/vm/levm/src/opcode_handlers/exchange.rs:18
		Ok(OpcodeResult::Continue)
	mov word ptr [rax], 25
		// crates/vm/levm/src/opcode_handlers/exchange.rs:19
		}
	vzeroupper
	ret

@iovoid
Copy link
Contributor Author

iovoid commented Nov 27, 2025

The noteworthy difference being the absence of cmp rax, 1024 and the accompanying branch to LBB480_3 (which throws a panic due to the bounds check failure)

@github-project-automation github-project-automation bot moved this to In Review in ethrex_l1 Nov 28, 2025
@iovoid iovoid enabled auto-merge November 28, 2025 19:16

### 2025-11-27

- Use unchecked swap for stack [#5439](https://github.com/lambdaclass/ethrex/pull/5439)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entry is misleading, as this no longer uses unchecked swap but a hint for the compiler.

Comment on lines +152 to +154
unsafe {
assert_unchecked(self.offset < STACK_LIMIT)
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a comment explaining that this removes a bound check in the call to swap.

@iovoid iovoid added this pull request to the merge queue Nov 28, 2025
Merged via the queue into main with commit 80ea03f Nov 28, 2025
58 checks passed
@iovoid iovoid deleted the unchecked_swap branch November 28, 2025 20:03
@github-project-automation github-project-automation bot moved this from In Review to Done in ethrex_l1 Nov 28, 2025
@github-project-automation github-project-automation bot moved this from Todo to Done in ethrex_performance Nov 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

levm Lambda EVM implementation performance Block execution throughput and performance in general

Projects

Status: Done
Archived in project

Development

Successfully merging this pull request may close these issues.

5 participants