Skip to content

Implement DUPN/SWAPN/EXCHANGE (EIP-8024)#1429

Merged
chfast merged 29 commits intoipsilon:masterfrom
frangio:eip-8024
Apr 14, 2026
Merged

Implement DUPN/SWAPN/EXCHANGE (EIP-8024)#1429
chfast merged 29 commits intoipsilon:masterfrom
frangio:eip-8024

Conversation

@frangio
Copy link
Copy Markdown
Contributor

@frangio frangio commented Jan 30, 2026

Implements the DUPN (0xe6), SWAPN (0xe7), and EXCHANGE (0xe8) stack instructions for the Amsterdam revision as specified in EIP-8024 (c0aa8d7).

Changes

Opcodes & traits (instructions_opcodes.hpp, instructions_traits.hpp, instructions_xmacro.hpp):

  • Register OP_DUPN, OP_SWAPN, OP_EXCHANGE with gas cost 3, activation at EVMC_AMSTERDAM.
  • stack_height_change=+1 for DUPN enables the overflow check in check_requirements.
  • stack_height_required=0 for all three — underflow depends on the decoded immediate and is checked in each handler.

Immediate decoding (instructions.hpp):

  • decode_dupn_swapn_imm()std::optional<int>: validates the forbidden range (0x5b..0x7f) and decodes n = (imm + 0x91) mod 256 in one step.
  • decode_exchange_imm()std::optional<std::pair<int, int>>: validates the forbidden range (0x52..0x7f) and decodes (n, m) via XOR + divmod-16 grid.

Handlers (instructions.hpp):

  • dupn: decode, check underflow (n > stack_size), push stack[n-1].
  • swapn: decode, check underflow (n >= stack_size), fast_swap(top, stack[n]).
  • exchange: decode, check underflow (m >= stack_size), fast_swap(stack[n], stack[m]).
  • fast_swap uses the manual 4-limb copy to match the existing swap<N> workaround for clang (llvm#59116).

Advanced interpreter (advanced_instructions.cpp):

  • Opcodes mapped to op_undefined — not implemented in advanced (per maintainer decision).

JUMPDEST analysis: Unchanged. The EIP's forbidden immediate range ensures 0x5b after these opcodes always causes an invalid-immediate halt, preserving 0x5b as a valid JUMPDEST.

Tests

  • Basic execution, end-of-code (implicit 0x00 immediate), invalid immediates (single + boundary sweep), stack overflow/underflow for all three opcodes, gas boundary (OOG at gas-1, success at exact gas).
  • exchange_max_m: regression for decode off-by-one at (n=1, m=29) — the same bug that caused Nethermind's consensus failure on bal-devnet-2.
  • dupn_immediate_0x5b_is_jumpdest: JUMPDEST compatibility — 0x5b in the immediate position is a valid jump target in both Amsterdam and pre-Amsterdam.
  • push1_stack_overflow: regression ensuring the stack overflow check is not bypassed for PUSH instructions.
  • Pre-Amsterdam undefined instruction tests (evm_undefined_instructions_test.cpp) updated from EVMC_MAX_REVISION to EVMC_OSAKA.

Comment thread lib/evmone/advanced_analysis.cpp Outdated
@frangio frangio force-pushed the eip-8024 branch 2 times, most recently from 3fc2e51 to d7c8b12 Compare January 30, 2026 20:40
@frangio frangio marked this pull request as ready for review January 30, 2026 20:44
@frangio frangio marked this pull request as draft January 30, 2026 20:51
@frangio frangio marked this pull request as ready for review January 30, 2026 21:05
@chfast chfast changed the title Implement EIP-8024 Implement DUPN/SWAPN/EXCHANGE (EIP-8024) Feb 17, 2026
@frangio frangio requested a review from chfast March 11, 2026 13:48
chfast added a commit that referenced this pull request Apr 10, 2026
Implement the DUPN, SWAPN, and EXCHANGE instructions for the Amsterdam
revision as specified in EIP-8024.

Based on #1429, converted from EVMC_EXPERIMENTAL to
EVMC_AMSTERDAM.

https://eips.ethereum.org/EIPS/eip-8024
chfast added a commit that referenced this pull request Apr 10, 2026
Implement the DUPN, SWAPN, and EXCHANGE instructions for the Amsterdam
revision as specified in EIP-8024.

Based on #1429, converted from EVMC_EXPERIMENTAL to
EVMC_AMSTERDAM.

https://eips.ethereum.org/EIPS/eip-8024
frangio and others added 17 commits April 13, 2026 15:53
# Conflicts:
#	lib/evmone/instructions_traits.hpp
The immediate_size-based guard skipped stack overflow checks for all
instructions with immediates, including PUSH1..PUSH32. Use an explicit
opcode check for DUPN/SWAPN/EXCHANGE instead.

Fix the dupn_implicit_immediate test which used an invalid immediate
due to ret_top() bytes following the DUPN opcode.
The existing swap<N> template avoids std::swap due to a clang missed
optimization (llvm/llvm-project#59116). Apply the same manual
component-wise swap in swapn and exchange for consistency.
Replace two-comparison validity check (x <= 0x5a || x >= 0x80) with
a single unsigned range check on the invalid range. Compiles to one
sub + one cmp instead of two cmp + or.
Replace the q < r branch with a sign-bit mask to select between the
two triangle halves of the (n,m) pair space. Eliminates a branch
in the EXCHANGE hot path.

Verified exhaustively against the branching version for all 210
valid immediate values.
- SWAPN invalid immediate boundary sweep: all boundary values of the
  forbidden range (0x5b..0x7f) plus adjacent valid values (0x5a, 0x80).
- DUPN stack overflow: fill stack to 1024 items then DUPN (which pushes).
Coverage analysis showed the stack underflow branches in swapn and
exchange handlers were never taken. Add dedicated tests to achieve
100% branch coverage of all three EIP-8024 instruction handlers.
Replace separate is_valid + decode calls with decode_dupn_swapn_imm()
and decode_exchange_imm() that return std::optional. Returns nullopt
for invalid immediates, eliminating the separate validation step.
chfast added 7 commits April 14, 2026 09:36
This reverts commit 622d97287f9459f3392d10b99a772b398b9d5ce9.

# Conflicts:
#	test/utils/bytecode.hpp
The cleanup in 240e710 changed `static_cast<uint8_t>(imm + 145)` to
`imm + 145`, losing the modular wrap. For imm >= 111 (e.g. 0x80=128),
the result exceeds 255 instead of wrapping to the correct depth
(128+145=273 instead of 17). Restore the uint8_t cast.
Replace decimal 145 with hex 0x91 for consistency with the other hex
constants in the EIP-8024 encoding. Both GCC and clang already
generate optimal code (branchless on clang, single range check on GCC).
- dupn_stack_overflow: document the UB risk in baseline invoke() when
  stack pointer is adjusted past the array end on DUPN overflow error.
- dupn/swapn/exchange_out_of_gas: verify OOG at exactly gas-1 and
  success at exact gas cost.
- Add EIP8024_TEST macro to eliminate repeated is_advanced()/rev boilerplate
- Group tests by opcode (DUPN, SWAPN, EXCHANGE) with section comments
- Remove redundant local variables where execute() is called once
- Add push1_stack_overflow_at_amsterdam regression test
- Consistent comment style: imm=0xNN → decoded value
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for EIP-8024 stack instructions (DUPN, SWAPN, EXCHANGE) to evmone by introducing the new opcodes, wiring them into the opcode/traits tables, implementing the baseline interpreter handlers (including immediate decoding), and adding dedicated unit tests for expected behavior and edge cases.

Changes:

  • Add opcode identifiers for 0xe6..0xe8 (DUPN/SWAPN/EXCHANGE) and register them in the opcode xmacro.
  • Define gas costs + traits (immediate size, stack delta, activation revision) and implement baseline execution handlers with immediate decoding + stack checks.
  • Add a new unit test suite for EIP-8024 and adjust “undefined before activation” tests to use EVMC_OSAKA.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/unittests/instructions_test.cpp Extends traits validation to assert 1-byte immediates for DUPN/SWAPN/EXCHANGE.
test/unittests/evm_undefined_instructions_test.cpp Updates pre-activation undefined-instruction checks to run at EVMC_OSAKA.
test/unittests/evm_eip8024_swapn_dupn_exchange_test.cpp Adds baseline-focused EIP-8024 behavior/edge-case tests (skipping Advanced).
test/unittests/CMakeLists.txt Includes the new EIP-8024 unit test source in the unit test target.
lib/evmone/instructions_xmacro.hpp Changes 0xe6–0xe8 from undefined opcodes to named opcode identifiers.
lib/evmone/instructions_traits.hpp Adds Amsterdam gas costs and traits entries for the new opcodes.
lib/evmone/instructions_opcodes.hpp Introduces OP_DUPN, OP_SWAPN, OP_EXCHANGE enum values.
lib/evmone/instructions.hpp Adds immediate decoders and baseline core implementations for DUPN/SWAPN/EXCHANGE.
lib/evmone/baseline_execution.cpp Skips generic stack checks for these opcodes (stack depth depends on immediate).
lib/evmone/advanced_instructions.cpp Forces these opcodes to op_undefined in the Advanced interpreter table.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/evmone/advanced_instructions.cpp
Comment thread lib/evmone/instructions.hpp
chfast added 3 commits April 14, 2026 13:15
The if constexpr guard excluding DUPN/SWAPN/EXCHANGE from stack checks
is not needed because the traits already encode the correct behavior:
- DUPN: stack_height_change=+1 enables overflow check (correct).
- SWAPN/EXCHANGE: stack_height_change=0, stack_height_required=0,
  so both checks are compile-time eliminated.

This also fixes a UB issue: the overflow check now runs in
check_requirements (before invoke), preventing the stack pointer
from being advanced past the array end on DUPN overflow.

Remove the now-redundant overflow check from the dupn handler.
- Revert to TEST_P(evm, ...) with explicit rev/advanced guards
- Add gas boundary tests (dupn/swapn/exchange_out_of_gas)
- Add stack underflow tests for SWAPN and EXCHANGE
- Add push1_stack_overflow_at_amsterdam regression test
- Group tests by opcode with section comments
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/evmone/instructions_traits.hpp
Comment thread test/unittests/evm_eip8024_swapn_dupn_exchange_test.cpp
chfast added 2 commits April 14, 2026 13:48
Test that 0x5b in the immediate position of DUPN is a valid JUMPDEST
in both Amsterdam (where DUPN is defined but 0x5b is a forbidden
immediate) and pre-Amsterdam (where 0xe6 is an undefined opcode).
JUMPDEST analysis is unchanged by EIP-8024.
@chfast chfast merged commit 1ed243d into ipsilon:master Apr 14, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants