From a7b7bcfed6186da1b41b7e0a36c26196e9d3696f Mon Sep 17 00:00:00 2001 From: Greg Colvin Date: Tue, 21 Jan 2020 21:14:17 -0700 Subject: [PATCH 1/6] subroutines --- EIPS/eip-2315.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 EIPS/eip-2315.md diff --git a/EIPS/eip-2315.md b/EIPS/eip-2315.md new file mode 100644 index 0000000000000..b0b89fe2efd8a --- /dev/null +++ b/EIPS/eip-2315.md @@ -0,0 +1,91 @@ +--- +eip: 2315 +title: Simple Subroutines for the EVM +status: Draft +type: Standards Track +category: Core +author: Greg Colvin +discussions-to: https://github.com/ethereum/EIPs/issues/2315 +created: 2019-10-17 +--- +## Abstract + +This proposal introduces two opcodes to support subroutines: `JUMPSUB` and `RETSUB`. + +## Motivation + +The EVM does not provide subroutines as a primitive. Instead, calls must be synthesized by fetching and pushing the current program counter on the data stack and jumping to the subroutine address; returns must be synthesized by getting the return address to the top of stack and jumping back to it. + +## Specification + +**_JUMPSUB_** + +* Jumps to the address on top of the stack. This must be a `JUMPDEST`. + +**_RETSUB_** + +* Returns to the most recently executed `JUMPSUB` instruction. + +## Rationale + +This is the smallest possible change that provides native subroutines without breaking backwards compatibility. It does not enforce validity like [EIP-615](https://eips.ethereum.org/EIPS/eip-615), but code that +* pushes a constant argument before every `JUMP`, `JUMPI` and `JUMPSUB` +* ensures that the _data stack_ is the same size at every execution of each `JUMP`, `JUMPI`, `JUMPSUB` or `JUMPDEST` operation. + +is valid and will meet all of the [EIP-615](https://eips.ethereum.org/EIPS/eip-615) safety conditions. In particular, the Yellow paper defines five exceptional halting states. +1. Insufficient gas +2. More than 1024 stack items +3. Insufficient stack items +4. Invalid jump destination +5. Invalid instruction + +Valid code cannot violate states 3, 4, or 5. + +## Backwards Compatibility + +These changes do not affect existing EVM code. + +## Test Cases +``` +PUSH 4 // before jumpsub: PC = 3 +JUMPSUB // after jumpsub: PC = 4 +JUMPDEST // before retsub: PC = 4 +RETSUB // after retsub: PC = 3 +``` +## Implementations + +No clients have implemented this proposal as of yet. Constraints on all implementation are given here. + +Jumps to and returns from subroutines are described here in terms of +* The EVM _bytecode array_ and _data stack_, (as defined in the [Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf)). +* The EVM _program counter_ `PC` which is the byte offset of the currently executing instruction in the _bytecode_array_. +* A new _return stack_ of up to 1024 _program counters_. + +The new execution semantics proposed her are implemented by the following pseudocode, which adds cases for `JUMPSUB` and `RETSUB` to a simple loop-and-switch interpreter. +``` + while PC < size(bytecode) { + switch opcode = bytecode[PC] { + ... + case JUMPSUB: + push(return_stack, PC) + PC = pop(data_stack) + case RETSUB: + PC = pop(return_stack) + ... + } + ++PC + } +``` +Execution of EVM bytecode begins with one value on the return stack—the size of the bytecode. The virtual byte of 0 at this offsetis the EVM to `STOP` opcode. Thus executing a `RETSUB` with no prior `JUMPSUB` executes a `STOP`. A `STOP` or `RETURN` ends the execution of the subroutine and the program. + +We suggest the cost of `JUMPSUB` should be _low_, and `RETSUB` should be _verylow_. + Measurement will tell. We suggest the following opcodes: +``` +0xbe JUMPSUB +0xbf RETSUB +``` +## Security Considerations + +This proposal introduces no new security considerations to the EVM. + +**Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).** From dab3d4a21d5dc17b1ac153e0b2dde1b4942876d0 Mon Sep 17 00:00:00 2001 From: Greg Colvin Date: Tue, 21 Jan 2020 21:42:36 -0700 Subject: [PATCH 2/6] redirect discussions to new magicians thread --- EIPS/eip-2315.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2315.md b/EIPS/eip-2315.md index b0b89fe2efd8a..919ab23aa5520 100644 --- a/EIPS/eip-2315.md +++ b/EIPS/eip-2315.md @@ -5,7 +5,7 @@ status: Draft type: Standards Track category: Core author: Greg Colvin -discussions-to: https://github.com/ethereum/EIPs/issues/2315 +discussions-to: https://ethereum-magicians.org/t/eip-2315-simple-subroutines-for-the-evm/3941 created: 2019-10-17 --- ## Abstract From de49e349caba464b4ee24a7287a3a655f41d1357 Mon Sep 17 00:00:00 2001 From: Greg Colvin Date: Wed, 22 Jan 2020 01:03:53 -0700 Subject: [PATCH 3/6] yet more concise --- EIPS/eip-2315.md | 75 ++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/EIPS/eip-2315.md b/EIPS/eip-2315.md index 919ab23aa5520..6eaa5f300349c 100644 --- a/EIPS/eip-2315.md +++ b/EIPS/eip-2315.md @@ -18,65 +18,60 @@ The EVM does not provide subroutines as a primitive. Instead, calls must be syn ## Specification -**_JUMPSUB_** +##### `JUMPSUB` +Jumps to the address on top of the stack, which must be a `JUMPDEST`. -* Jumps to the address on top of the stack. This must be a `JUMPDEST`. - -**_RETSUB_** - -* Returns to the most recently executed `JUMPSUB` instruction. +##### `RETSUB` +Returns to the most recently executed `JUMPSUB` instruction. ## Rationale -This is the smallest possible change that provides native subroutines without breaking backwards compatibility. It does not enforce validity like [EIP-615](https://eips.ethereum.org/EIPS/eip-615), but code that -* pushes a constant argument before every `JUMP`, `JUMPI` and `JUMPSUB` -* ensures that the _data stack_ is the same size at every execution of each `JUMP`, `JUMPI`, `JUMPSUB` or `JUMPDEST` operation. +This is the smallest possible change that provides native subroutines without breaking backwards compatibility. -is valid and will meet all of the [EIP-615](https://eips.ethereum.org/EIPS/eip-615) safety conditions. In particular, the Yellow paper defines five exceptional halting states. -1. Insufficient gas -2. More than 1024 stack items -3. Insufficient stack items -4. Invalid jump destination -5. Invalid instruction +This proposal does not enforce validity like [EIP-615](https://eips.ethereum.org/EIPS/eip-615) + - having very little to enforce - but code that +* pushes a constant argument before every `JUMP`, `JUMPI` and `JUMPSUB`, +* ensures that the _data stack_ is the same size at every execution of each `JUMP`, `JUMPI`, `JUMPSUB` or `JUMPDEST` operation, +* and meets some other obvious conditions -Valid code cannot violate states 3, 4, or 5. +will meet all of the [EIP-615](https://eips.ethereum.org/EIPS/eip-615) safety conditions. ## Backwards Compatibility -These changes do not affect existing EVM code. +These changes do not affect the semantics of existing EVM code. ## Test Cases ``` -PUSH 4 // before jumpsub: PC = 3 -JUMPSUB // after jumpsub: PC = 4 -JUMPDEST // before retsub: PC = 4 -RETSUB // after retsub: PC = 3 +0: PUSH 3 // before jumpsub: PC = 1; SP = 1 +1: JUMPSUB // after jumpsub: PC = 3; SP = 0 +2 PUSH 5 // after push: PC =3; SP =1 +3: JUMPDEST // before retsub: PC = 3; SP = 0 +4: RETSUB // after retsub: PC = 1; SP = 0 +5: STOP ``` ## Implementations -No clients have implemented this proposal as of yet. Constraints on all implementation are given here. - -Jumps to and returns from subroutines are described here in terms of -* The EVM _bytecode array_ and _data stack_, (as defined in the [Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf)). -* The EVM _program counter_ `PC` which is the byte offset of the currently executing instruction in the _bytecode_array_. -* A new _return stack_ of up to 1024 _program counters_. +No clients have implemented this proposal as of yet. -The new execution semantics proposed her are implemented by the following pseudocode, which adds cases for `JUMPSUB` and `RETSUB` to a simple loop-and-switch interpreter. +The new operators proposed here are implemented by the following pseudocode, which adds cases for `JUMPSUB` and `RETSUB` to a simple loop-and-switch interpreter. ``` - while PC < size(bytecode) { - switch opcode = bytecode[PC] { - ... - case JUMPSUB: - push(return_stack, PC) - PC = pop(data_stack) - case RETSUB: - PC = pop(return_stack) - ... - } - ++PC +bytecode[code_size] +data_stack[1024] +return_stack[1024] +while PC < code_size { + switch opcode = bytecode[PC] { + ... + case JUMPSUB: + push(return_stack, PC) + PC = pop(data_stack) + case RETSUB: + PC = pop(return_stack) + ... } + ++PC +} ``` -Execution of EVM bytecode begins with one value on the return stack—the size of the bytecode. The virtual byte of 0 at this offsetis the EVM to `STOP` opcode. Thus executing a `RETSUB` with no prior `JUMPSUB` executes a `STOP`. A `STOP` or `RETURN` ends the execution of the subroutine and the program. +Execution of EVM bytecode begins with one value on the return stack—the size of the bytecode. The virtual byte of 0 at this offset is the EVM `STOP` opcode, so executing a `RETSUB` with no prior `JUMPSUB` executes a `STOP`. A `STOP` or `RETURN` ends the execution of the subroutine and the program. We suggest the cost of `JUMPSUB` should be _low_, and `RETSUB` should be _verylow_. Measurement will tell. We suggest the following opcodes: From 3a3e47057d7bc29a736881a8fb2383e574d18d4f Mon Sep 17 00:00:00 2001 From: Greg Colvin Date: Wed, 22 Jan 2020 01:20:56 -0700 Subject: [PATCH 4/6] thanks holiman --- EIPS/eip-2315.md | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/EIPS/eip-2315.md b/EIPS/eip-2315.md index 6eaa5f300349c..34ad3d50c6fc5 100644 --- a/EIPS/eip-2315.md +++ b/EIPS/eip-2315.md @@ -28,26 +28,18 @@ Returns to the most recently executed `JUMPSUB` instruction. This is the smallest possible change that provides native subroutines without breaking backwards compatibility. -This proposal does not enforce validity like [EIP-615](https://eips.ethereum.org/EIPS/eip-615) - - having very little to enforce - but code that -* pushes a constant argument before every `JUMP`, `JUMPI` and `JUMPSUB`, -* ensures that the _data stack_ is the same size at every execution of each `JUMP`, `JUMPI`, `JUMPSUB` or `JUMPDEST` operation, -* and meets some other obvious conditions - -will meet all of the [EIP-615](https://eips.ethereum.org/EIPS/eip-615) safety conditions. - ## Backwards Compatibility These changes do not affect the semantics of existing EVM code. ## Test Cases ``` -0: PUSH 3 // before jumpsub: PC = 1; SP = 1 -1: JUMPSUB // after jumpsub: PC = 3; SP = 0 -2 PUSH 5 // after push: PC =3; SP =1 -3: JUMPDEST // before retsub: PC = 3; SP = 0 -4: RETSUB // after retsub: PC = 1; SP = 0 -5: STOP +step op stack +0 PUSH1 3 [] +1 JUMPSUB [3] +4 STOP +2 JUMPDEST [] +3 RETSUB [] ``` ## Implementations From 457abdf1555bba27a5ca0c3a99b79ea75b83febb Mon Sep 17 00:00:00 2001 From: Greg Colvin Date: Wed, 22 Jan 2020 01:35:29 -0700 Subject: [PATCH 5/6] thanks again holiman --- EIPS/eip-2315.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2315.md b/EIPS/eip-2315.md index 34ad3d50c6fc5..0cc0ff084a460 100644 --- a/EIPS/eip-2315.md +++ b/EIPS/eip-2315.md @@ -19,7 +19,7 @@ The EVM does not provide subroutines as a primitive. Instead, calls must be syn ## Specification ##### `JUMPSUB` -Jumps to the address on top of the stack, which must be a `JUMPDEST`. +Jumps to the address on top of the stack, which must be the offset of a `JUMPDEST`. ##### `RETSUB` Returns to the most recently executed `JUMPSUB` instruction. From 4ab57c4206360a4961ed475567aecba096f14f02 Mon Sep 17 00:00:00 2001 From: Greg Colvin Date: Sat, 1 Feb 2020 21:15:18 -0700 Subject: [PATCH 6/6] specify stack size, require jumpdest target for retsub, fix testcase and pseudocode --- EIPS/eip-2315.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/EIPS/eip-2315.md b/EIPS/eip-2315.md index 0cc0ff084a460..2b22c29c199e0 100644 --- a/EIPS/eip-2315.md +++ b/EIPS/eip-2315.md @@ -22,7 +22,9 @@ The EVM does not provide subroutines as a primitive. Instead, calls must be syn Jumps to the address on top of the stack, which must be the offset of a `JUMPDEST`. ##### `RETSUB` -Returns to the most recently executed `JUMPSUB` instruction. +Returns to the instruction after the most recently executed `JUMPSUB` instruction, which must be a `JUMPDEST` + +A program may jump at most 1024 times without returning. ## Rationale @@ -37,10 +39,13 @@ These changes do not affect the semantics of existing EVM code. step op stack 0 PUSH1 3 [] 1 JUMPSUB [3] -4 STOP +4 JUMPDEST [] +5 STOP [] 2 JUMPDEST [] 3 RETSUB [] ``` +This code should terminate after 5 steps with an empty stack. + ## Implementations No clients have implemented this proposal as of yet. @@ -50,15 +55,20 @@ The new operators proposed here are implemented by the following pseudocode, whi bytecode[code_size] data_stack[1024] return_stack[1024] +push(return_stack, PC) +PC = 0 while PC < code_size { - switch opcode = bytecode[PC] { + opcode = bytecode[PC] + ... + switch opcode { ... case JUMPSUB: - push(return_stack, PC) - PC = pop(data_stack) + push(return_stack, PC + 1) + PC = pop(data_stack) + continue case RETSUB: - PC = pop(return_stack) - ... + PC = pop(return_stack) + continue } ++PC } @@ -75,4 +85,4 @@ We suggest the cost of `JUMPSUB` should be _low_, and `RETSUB` should be _verylo This proposal introduces no new security considerations to the EVM. -**Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).** +**Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).** \ No newline at end of file