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

After EIP-3855

Who is affected

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.

Original EIP · ← EIPs