Can any JUMP* go into any subroutine? Also can JUMP from within a subroutine go into another subroutine?
What happens in the case of:
JUMPSUB
JUMP (and jumping into another subroutine)
RETURNSUB
One would assume RETURNSUB picks up the last item from the return_stack. However from an analysis point of view in this case a RETURNSUB within a subroutine block may not only mean returning from that block, but returning from within some other block.
Yes, RETURNSUB must always return to the address last pushed on the stack and pop it @axic, regardless of the block it’s in. So in your example the RETURNSUB is unreachable, and the code will return to the JUMPSUB when and if it hits some other RETURNSUB. I think that will amount to a loop until the code jumped to stops somehow.
This proposal doesn’t constrain the structure of the code much at all, it just provides an efficient mechanism.
If I see correctly this kind of relies/follows the recommendations from EIP-2315 "Simple Subroutines for the EVM" - Analysis, because it relies on not-flowing into beginsub (at least in one place in the assembler)
Indeed! As the linked analysis article correctly states, flowing into a subroutine is not a feature a code generator would use. So I did not really follow the recommendations, I just did it in a clean way which is the same as what is recommended in the analysis
There is some inconsistency between the test case and the spec in EIP-2315.
The spec says that BEGINSUB is not supposed to be executed and its execution will cause error. JUMPSUB will land on the next instruction after BEGINSUB.
However, the test case (and the current open-ethereum implementation) assumes BEGINSUB can be executed as a noop. JUMPSUB will land on the BEGINSUB instead of the next.
Note that I believe the spec makes more sense from the security perspective. It prevents unintended control flow behavior in EVM crossing routine boundaries.
List of issues:
BEGINSUB should not be executed (or return OUT_OF_GAS). But the last test case in the EIP spec (Subroutine at end of code) counts it executed with gas 1.
The test case in openthereum also assumes BEGINSUB can be executed. If following the spec, it should be something like this:
A further comment on this: I think there is some logic behind putting Cost and Codes under Implementation in https://eips.ethereum.org/EIPS/eip-2315, but usually EIPs put these definitions into the Specification and that is where most people expect to find them.
(Cost and Codes currently contains the opcode numbers and the gas costs.)
Current costs are defined as "BEGINSUB be base (2) , JUMPSUB be mid (8) , and RETURNSUB be verylow (3)"
@holiman suggests: "I don’t see why RETURNSUB should be so cheap. I’d actually prefer it to be same as JUMPSUB – or, more specifically, that cost of(JUMPSUB+RETURNSUB) == cost of (JUMP + JUMP) . Which currently would put it at mid == 8"
I suggest that since JUMPSUB and RETURNSUB both need to push to/pop from the return_stack, they should be more expensive than JUMP (mid). Maybe the difference is not measurable too much, but still they should not be the same. I suggest mid + 1 or mid + 2 as a hunch.
I would argue that a RETURNSUB is inherently cheaper, since it doesn’t have to validate the destination. It just needs to pop a stack and set PC. I mean POP is cheap (pops one). So my hunch is 10 for JUMPSUB and maybe 3 or 5 for RETURNSUB.