EIP-3855 Introducing the PUSH0 instruction (PUSH0 instruction)
Final · EVM & execution · EIP status Final · created 2021-02-19
Adds PUSH0 (0x5f), a 1-byte instruction that pushes the constant 0 onto the stack, to the EVM. It costs less gas than the existing PUSH1 0 and makes code shorter.
Why it is needed
Many EVM instructions take offset values as input, and these are often 0. For example, the return data parameters of the CALL family of instructions are set to 0 by contracts that use the RETURNDATA family of instructions. Currently, pushing 0 onto the stack requires PUSH1 0, which costs 3 gas at execution and takes 2 bytes of code, so deployment also costs 2 * 200 gas. To save this cost, it is common to use other instructions such as PC, MSIZE, CALLDATASIZE, RETURNDATASIZE, CODESIZE, CALLVALUE and SELFBALANCE as a stand-in for 0, which is risky because these values can vary with context. A mainnet analysis found that about 11.5% of executed PUSH-family instructions pushed 0, and across all existing accounts 340,557,331 bytes were spent on PUSH1 00, costing 68,111,466,200 gas to deploy. Creating a dedicated zero instruction makes code shorter and reduces such workarounds.
What changes
Today
- Pushing 0 onto the stack requires PUSH1 0, which costs 3 gas at execution and 2 bytes of code.
- The workaround of using instructions such as CALLDATASIZE and RETURNDATASIZE as a stand-in for 0 to save cost is widespread.
- About 11.5% of executed PUSH-family instructions are used to push the value 0.
After EIP-3855
- A single PUSH0 (1 byte) pushes 0, and the execution cost is 2 gas.
- With a dedicated zero instruction, there is less reason to use other instructions as a stand-in for 0, and changing the price or behavior of those instructions later becomes less risky.
- Code that does the same job gets shorter, which lowers deployment cost.
Who is affected
- Smart contract developers: If the compiler uses PUSH0, contract code size and deployment and execution gas decrease.
- Client teams: Opcode 0x5f must be newly implemented. It sits in a contiguous space with the existing PUSH implementations, so the implementation can be shared.
- Users: They benefit indirectly from the lower cost of deploying and executing contracts.
- Existing deployed contracts: Contracts that already contain the previously undefined 0x5f in their code may behave differently after this EIP.
In depth
Instruction specification: 0x5f, no immediate data, 2 gas
The PUSH0 instruction is placed at opcode 0x5f. It has no trailing immediate data, pops nothing from the stack, and pushes one item with the value 0 onto the stack. Its gas cost is 2 gas, which falls in the base tier. PUSH1 0, which did the same job, cost 3 gas at execution and took 2 bytes of code, so using PUSH0 saves 1 gas at execution and 1 byte of code, and since the code is 1 byte shorter, deployment cost also drops by 200 gas per byte.
Motivation: mainnet measurements and three goals
The source analyzes mainnet block ranges 8,567,259…8,582,058 and 12,205,970…12,817,405 and presents the finding that about 11.5% of executed PUSH-family instructions push the value 0. Across all existing accounts, 340,557,331 bytes were spent on PUSH1 00, and deploying them cost 68,111,466,200 gas. On this basis it lists three goals. First, reduce contract code size. Second, reduce the risk of misusing instructions such as PC or RETURNDATASIZE as a stand-in for 0, because changing the price or behavior of such instructions becomes riskier. As an example, the source cites the case where the behavior of RETURNDATASIZE changes so that it is no longer guaranteed to be 0 at the start of a call frame. Third, reduce the need to use DUP instructions to duplicate 0.
Rationale for gas cost and opcode choice
2 gas (base) is the cost tier applied to instructions that push a constant value onto the stack, such as ADDRESS and ORIGIN. PUSH0 also always pushes the same value 0, so it uses the same tier. Since it does not need to read immediate data, a tier cheaper than PUSH1 0, which costs 3 gas, is natural. The opcode 0x5f was chosen because it sits in a contiguous space with the rest of the PUSH instructions. The source explains that this lets clients potentially share the implementation with the PUSH family.
Test cases
The source presents three tests. First, running the code 5F alone succeeds and leaves a single 0 on the stack. Second, running code with 5F repeated 1024 times succeeds and leaves 1024 zeros on the stack. Third, code with 5F repeated 1025 times halts execution with out of stack. This setup confirms that PUSH0 is subject to the stack depth limit just like any other instruction.
Backward compatibility, security, and current status
This EIP defines an opcode that did not previously exist. Therefore, existing deployed contracts that already contain 0x5f in their code may behave differently after this EIP. On security, the authors state that there are no known effects. In particular, PUSH0 has no immediate data bytes, so it does not affect jumpdest-analysis. The source status is Final, meaning the specification is finalized and will no longer change.