Introduces instruction for chaining function calls.
|Andrei Maiboroda (@gumb0), Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Matt Garnett (@lightclient)
Table of Contents
This EIP allows for tail call optimizations in EOF functions (EIP-4750) by introducing a new instruction
JUMPF, which jumps to a code section without adding a new return stack frame.
Additionally the format of the type sections is extended to allow declaring sections as non-returning, with simplified stack validation for
JUMPF to such section.
It is common for functions to make a call at the end of the routine only to then return.
JUMPF optimizes this behavior by changing code sections without needing to update the return stack.
Knowing at validation time that a function will never return control allows for
JUMPF to such function to be treated similar to terminating instructions, where extra items may be left on the operand stack at execution termination. This provides opportunities for compilers to generate more optimal code, both in code size and in spent gas. It is particularly benefitial for small error handling helpers, that end execution with
REVERT: they are commonly reused in multiple branches and extracting them into a helper function is efficient, when there is no need to pop extra stack items before
JUMPF to such helper.
We define non-returning section as the one that can not return control (via
RETF instruction) to the caller section.
outputs field contains a special value
0x80 when corresponding code section is non-returning. See Non-returning status validation below for validation details.
The first code section MUST have 0 inputs and be non-returning.
A new instruction,
JUMPF (0xe5), is introduced.
JUMPFhas one immediate argument,
target_section_index, encoded as a 16-bit unsigned big-endian value.
- If the operand stack size exceeds
1024 - type[target_section_index].max_stack_height(i.e. if the called function may exceed the global stack height limit), execution results in an exceptional halt. This guarantees that the target function does not exceed global stack height limit.
0, but does not change the return stack. Execution continues in the target section.
JUMPFcosts 5 gas.
JUMPFneither pops nor pushes anything to the operand stack.
Let the definition of
type[i] be inherited from EIP-4750 and define
stack_height to be the height of the stack at a certain instruction during the instruction flow traversal if the operand stack at the start of the function were equal to
- The immediate argument of
JUMPFMUST be less than the total number of code sections.
- For each
type[current_section_index].outputsMUST be greater or equal
- The stack height validation at
JUMPFdepends on whether the target section is non-returning:
JUMPFinto returning section (
type[target_section_index].outputsdoes not equal
0x80): stack height MUST be equal to
type[current_section_index].outputs + type[target_section_index].inputs - type[target_section_index].outputs. This means that target section can output less stack elements than the original code section called by the top element on the return stack, if the current code section leaves the delta
type[current_section_index].outputs - type[target_section_index].outputselement(s) on the stack.
JUMPFinto non-returning section (
0x80): stack height must be greater or equal than
JUMPFis considered terminating instruction, i.e. does not have successor instructions in code validation and MAY be final instruction in the section.
- The code validation defined in EIP-4200 also fails if any
RJUMP*offset points to one of the two bytes directly following a
CALLF instruction validation is extended to include the rule:
- Code section is invalid in case an immediate argument
CALLFtargets a non-returning section, i.e.
Section type MUST be non-returning in case the section contains no
RETF instructions and no
JUMPF instructions targeting returning sections (target section’s status is checked via its output value in type section.)
Note: This implies that section containing only
JUMPFs into non-returning sections is non-returning itself.
As long as
JUMPF prepares the delta
type[current_section_index].outputs - type[target_section_index].outputs stack elements before changing code sections, it is possible to jump to a section with less outputs than was originally entered via
CALLF. This will reduce duplicated code as it will allow compilers more flexibility during code generation such that certain helpers can be used generically by functions, regardless of their output values.
This change is backward compatible as EOF does not allow undefined instructions to be used or deployed, meaning no contracts will be affected.
Copyright and related rights waived via CC0.
Please cite this document as:
Andrei Maiboroda (@gumb0), Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Matt Garnett (@lightclient), "EIP-6206: EOF - JUMPF and non-returning functions [DRAFT]," Ethereum Improvement Proposals, no. 6206, December 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6206.