2

Contract

Take the following contract:

// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

contract Adder { function add(uint256 x, uint256 y) external pure returns (uint256 z) { unchecked { z = x + y; } } }

With the metadata hash disabled, the compiler optimizer turned on, and the number of optimizer runs set to 800, this is the output that I get:

Deployed Bytecode

6080604052348015600f57600080fd5b506004361060285760003560e01c8063771602f714602d575b600080fd5b603c6038366004604e565b0190565b60405190815260200160405180910390f35b60008060408385031215606057600080fd5b5050803592602090910135915056fea164736f6c6343000807000a

Opcodes

You can also see the opcodes nested vertically here.

PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x28 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x771602F7 EQ PUSH1 0x2D JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x3C PUSH1 0x38 CALLDATASIZE PUSH1 0x4 PUSH1 0x4E JUMP JUMPDEST ADD SWAP1 JUMP JUMPDEST PUSH1 0x40 MLOAD SWAP1 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH1 0x60 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP POP DUP1 CALLDATALOAD SWAP3 PUSH1 0x20 SWAP1 SWAP2 ADD CALLDATALOAD SWAP2 POP JUMP INVALID LOG1 PUSH5 0x736F6C6343 STOP ADDMOD SMOD STOP EXP

Source Maps

64:152:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;85:129;;;;;;:::i;:::-;192:5;;85:129;;;;413:25:1;;;401:2;386:18;85:129:0;;;;;;;14:248:1;82:6;90;143:2;131:9;122:7;118:23;114:32;111:52;;;159:1;156;149:12;111:52;-1:-1:-1;;182:23:1;;;252:2;237:18;;;224:32;;-1:-1:-1;14:248:1:o

Question

Reading the docs on Source Mappings, I understand that each assembly instruction is separated by a semicolon. So according to the source maps obtained above, the contract has 85 assembly instructions, since there are 84 semicolons.

In the opcodes string, there are 117 words. Out of those, 22 are PUSH1, one is PUSH4, and another one is PUSH5. Since PUSH instructions push values onto the stack, I calculated the number of assembly instructions like this:

117
- 22 x 1 // PUSH1
- 1  // PUSH4
- 1  // PUSH5
= 93

The logic being that the PUSHn opcode and the value it pushes is together considered an assembly instruction.

Now, as you can see, the results are not equal. The number of assembly instructions according to the source maps seems to be 85, but according to the opcodes string, seems to be 93.

To my knowledge, there is no opcode which "reduces" the number of assembly instructions, or which does not count as one.

What am I missing? What's the relationship between the opcodes and the number of semicolons in the source maps, if any?

Paul Razvan Berg
  • 17,902
  • 6
  • 73
  • 143
  • I considered another example (an example contract) and noticed a pattern: the difference between the number of assembly instructions generated via the two methods remains 8. I got 14 and 6, respectively, with this other example. That led me to conjecture that the first 8 instructions in the opcodes string are just a default collection to "bootstrap" the contract, but are not part of the assembly instructions belonging to the contract. – Paul Razvan Berg Sep 05 '21 at 16:19

0 Answers0