Search code examples
ethereum

What's the difference between solc's --opcodes and --asm?


I'm leanring low-level Solidity inline assembly, but confused by different output formats.

The ouptut option of Solidity compiler says:

  --asm                EVM assembly of the contracts.
  --opcodes            Opcodes of the contracts.

I tried both options to compile the contract below:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract MyContract {
    string bar = "Hello World";

    function foo() public view returns(string memory) {
        return bar;
    }
}

The command solc -o output --asm contract.sol generated MyContract.evm:

    /* "contract.sol":70:208  contract MyContract {... */
  mstore(0x40, 0x80)
    /* "contract.sol":96:122  string bar = "Hello World" */
  mload(0x40)
  dup1
  0x40
  add
  0x40
  mstore
  dup1
  0x0b
  dup2
  mstore
  0x20
  add
  0x7657399374655890603765000000000000000000000000000000000000000000
  dup2
  mstore
  pop
  0x00
  swap1
  dup1
  mload
  swap1
  0x20
  add
  swap1
  tag_1
  swap3
  swap2
  swap1
  tag_2
  jump  // in
tag_1:
  pop
    /* "contract.sol":70:208  contract MyContract {... */
  callvalue
  dup1
... (and a lot more code)

The command solc -o output --opcodes contract.sol generated MyContract.opcode:

PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0xB DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x7657399374655890603765000000000000000000000000000000000000000000 DUP2 MSTORE POP PUSH1 0x0 SWAP1 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 PUSH2 0x4F SWAP3 SWAP2 SWAP1 PUSH2 0x62 JUMP JUMPDEST POP CALLVALUE DUP1 ISZERO PUSH2 0x5C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x166 JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH2 0x6E SWAP1 PUSH2 0x105 JUMP JUMPDEST SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH2 0x90 JUMPI PUSH1 0x0 DUP6 SSTORE PUSH2 0xD7 JUMP JUMPDEST DUP3 PUSH1 0x1F LT PUSH2 0xA9 JUMPI DUP1 MLOAD PUSH1 ... ...

They look pretty much similar, although not 100% matching each other...

Questions

  1. Does --opcodes just give a more compact form of assembly code compared to --asm output?
  2. PUSH1 0x80 PUSH1 0x40 MSTORE in --opcodes format vs. mstore(0x40, 0x80) in --asm format. Are they doing the same thing? (I guess so but not 100% sure...)
  3. Is there a way to print --opcodes in a pretty format rather than in a single line?
  4. Are there any good resouces to learn Solidity inline assembly? I googled around but found a bunch of one page blogs, which are good to explain the basics but unfortunately none of them gives a complete and in-depth tutorial

Solution

  • 1. Does --opcodes just give a more compact form of assembly code compared to --asm output?

    That's somewhat correct. You could still translate the assembly to opcodes and get the same result.

    Assembly represents a set of "low level-ish" instructions. Opcodes are the "real" binary instructions passed to the EVM. See for example this table that translates the opcodes to binary.

    2. PUSH1 0x80 PUSH1 0x40 MSTORE in --opcodes format vs. mstore(0x40, 0x80) in --asm format. Are they doing the same thing? (I guess so but not 100% sure...)

    Yes, they are doing the same thing - see answer to 1. When you run this snippet in Solidity

    assembly {
        mstore(0x40, 0x80)
    }
    

    you get the same opcodes.

    Remix debugger

    3. Is there a way to print --opcodes in a pretty format rather than in a single line?

    You can use tr on any other text-formatting unix cli command.

    echo "PUSH1 0x80 PUSH1" | tr ' ' '\n'
    
    PUSH1
    0x80
    PUSH1
    

    See this forum post for more ways.

    4. Are there any good resouces to learn Solidity inline assembly?

    Apart from the documentation, I don't know any, so I'll let someone else give a better answer. But Solidity is still a pretty new technology, so my guess is that most people who specialize in Solidity assembly, learned by trial&error.