Subroutines (#3376)

* withdrawn

* static safety

* typos

* urls

* urls

* clarity, code bug

* syntax

* clarity, eplanations, bugs, references

* incorporate review so far

* remove external reference

* clarity

* move to Review

* indirect jumps
This commit is contained in:
Greg Colvin 2021-03-13 20:30:14 -05:00 committed by GitHub
parent 3eb9111c26
commit 4359573483
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -13,7 +13,7 @@ created: 2019-10-17
This proposal introduces three opcodes to support subroutines: `BEGINSUB`, `JUMPSUB` and `RETURNSUB`.
Safety and amenability to static analysis equivalent to EIP-615 can be ensured by following a few simple rules, and validated with the provided algorithm.
Safety and amenability to static analysis equivalent to EIP-615 can be ensured by enforcing a few simple rules, and validated with the provided algorithm.
## Motivation
@ -59,11 +59,16 @@ _Note 2: Values popped off the `return stack` do not need to be validated, since
_Note 3: The description above lays out the semantics of this feature in terms of a `return stack`. But the actual state of the `return stack` is not observable by EVM code or consensus-critical to the protocol. (For example, a node implementor may code `JUMPSUB` to unobservably push `pc` on the `return stack` rather than `pc + 1`, which is allowed so long as `RETURNSUB` observably returns control to the `pc + 1` location.)_
### Indirect Jumps
If [EIP-3337 BEGINDATA](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3337.md) is implemented then the indirect jumps from EIP-615 -- `JUMPV` and `JUMPSUBV` -- can be implemented. These would take two arguments on the stack: a constant offset relative to `BEGINDATA` to a jump table, and a variable index into that table. Detailed specifications can await the acceptance EIP-3337.
## Rationale
We modeled this design on the, simple, proven, [archetypal Forth virtual machine](https://users.ece.cmu.edu/~koopman/stack_computers/index.html) of [1970](http://www.ultratechnology.com/4th_1970.pdf). It is a two-stack design -- the data stack is supplemented with a return stack to support jumping into and returning from subroutines, as specified above. Most other models place the return address on the stack. The separate return stack ensures that the return address cannot be overwritten or mislaid, and obviates any need to swap the return address past the arguments on the stack.
We modeled this design on the, simple, proven, [archetypal Forth virtual machine](https://users.ece.cmu.edu/~koopman/stack_computers/index.html) of [1970](http://www.ultratechnology.com/4th_1970.pdf). It is a two-stack design -- the data stack is supplemented with a return stack to support jumping into and returning from subroutines, as specified above. Most other models place the return address on the stack.
Importantly, a dynamic jump is not needed to implement subroutine returns, allowing for deprecation of dynamic uses of JUMP and JUMPI. Deprecating dynamic jumps is key to practical static analysis of code.
The separate return stack ensures that the return address cannot be overwritten or mislaid, and obviates any need to swap the return address past the arguments on the stack. Importantly, a dynamic jump is not needed to implement subroutine returns, allowing for deprecation of dynamic uses of JUMP and JUMPI. Deprecating dynamic jumps is key to practical static analysis of code.
## Backwards and Forwards Compatibility
@ -119,7 +124,7 @@ _Execution_ is as defined in the [Yellow Paper](https://ethereum.github.io/yello
4. Invalid jump destination
5. Invalid instruction
Conditions 3, 4, and 5 cannot occur if the code conforms to following rules.
Conditions 1 and 2 -- Insufficient gas and stack overflow, must be checked at runtime. Conditions 3, 4, and 5 cannot occur if the code conforms to the following rules.
* `JUMP` and `JUMPI` address only valid `JUMPDEST` instructions.
* `JUMPSUB` addresses only valid `BEGINSUB` instructions.
@ -128,7 +133,7 @@ Valid instructions are not part of PUSH data.
* `JUMP`, `JUMPI` and `JUMPSUB` are always preceded by one of the `PUSH` instructions.
Requiring a `PUSH` before each `JUMP` forbids dynamic jumps. (This restriction could be relaxed with something like Wasm function tables, assuming we have [a place to put the tables](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3337.md)).
Requiring a `PUSH` before each `JUMP` forbids dynamic jumps. Absent dynamic jumps another mechanism is needed for subroutine returns, as provided here.
The `stack pointer` or `SP` points just past the top item on the `data stack`. We define the `stack depth` as the number of stack elements between the current `SP` and the current `stack base`. The `stack base` was the `SP` at the previous `JUMPSUB`, or `0` on program entry. So we can check for all stack underflows and some stack overflows.