3

I am trying to analyse the bytecode of creating a contract and I can't figure out why there is a need for the 006 DUP1 opcode in the snippet below.

000 PUSH1 80
002 PUSH1 40
004 MSTORE
005 CALLVALUE
006 DUP1
007 ISZERO
008 PUSH2 0010
011 JUMPI
012 PUSH1 00
014 DUP1
015 REVERT
016 JUMPDEST
017 POP
...

My understanding is that before executing 011 JUMPI the stack will have three items:

0010
[result of 007 ISZERO]
[value from 005 CALLVALUE]

011 JUMPI will take two items off the stack, leaving [value from 005 CALLVALUE]. The next instruction set will be either:

  • 012 PUSH1 00 -> 014 DUP1 -> 015 REVERT (this opcode takes two parameters, so it will take the original and duplicated 00 generated by 012 PUSH1 00; after that no more code will be executed)
  • 016 JUMPDEST -> 017 POP (at this point [value from 005 CALLVALUE] will be discarded) -> ...

It seems to me that in both cases the value is not used for anything, so 006 DUP1 can be safely removed from the generated bytecode. What am I missing?

EDIT:

This bytecode has been generated in Remix using the 0.6.4 compiler with optimization enabled.

3
  • If you are looking for the minimum code to deploy some byte code here is an example that deploys random byte code. It will not look like the code generated by the Solidity compiler but it's valid (I wrote it manually). github.com/cleanunicorn/ranploy/blob/master/ranploy/… Mar 22, 2020 at 21:08
  • The 006 DUP1 might be a compiler issue or non-optimized byte code. Mar 22, 2020 at 21:09
  • @DanielLucaCleanUnicorn I added information about the compiler that I used.
    – Kapol
    Mar 22, 2020 at 22:03

1 Answer 1

0

This piece of code is generated for a contract with a non-payable constructor to ensure that it is called with value 0, otherwise REVERT 0 0. It originates in ContractCompiler::appendCallValueCheck which generates the CALLVALUE and then does CompilerContext::appendInlineAssembly("{ if condition { revert(0, 0) } }", {"condition"});

CompilerContext::appendInlineAssembly is quite complex (barely readable, frankly) but long story short, "condition" here refers to the value on top of the stack, and since the stack machine must consume the value in order to test it, as a courtesy to other code generators, appendInlineAssembly duplicates it before testing it so that it leaves the state of the stack unchanged. Which is indeed not necessary here since no following code actually reads the value, but it is basically a convenience to keep the compiler code as generic as possible (taking any assembly you can throw at it and spitting out machine code is about as generic as it gets).

But you are right, in this case it doesn't really accomplish anything, and it and the redundant POP could be optimized out for a savings of 6 gas on running the constructor and 16 deployment gas (or indeed if you really decided you wanted a copy of the call value on the stack you could simply CALLVALUE again at any time for only 2 gas!). If you write everything in assembly there are quite a few optimizations like this you can make, but in most cases they're premature optimizations and you can save more gas by writing better Solidity.

I rewrote one 80-line Solidity contract that cost 151,000 deployment gas into assembly with Yul and got it down to 101,000 gas (about 1100 bytes down to 300). It was obviously pretty simple, but had a lot of operations on large arrays from calldata, and since all array accesses are bounds checked in Solidity even if it is impossible for them to be out of bounds, rewriting without the checks and optimizing the calldata to memory copying saved a huge amount of boilerplate utility code that took half the generated code (run solc with the --asm flag and that is all the #utility.yul stuff at the end). In the big picture one DUP and a POP aren't too big a deal! (Also obviously there are extreme security and correctness implications involved with bypassing the compiler's safety checks which are indeed there for a reason. Not recommended on production code for anyone without extensive experience writing stack machine assembly and EVM code in general, or maybe not anyone at all...but certainly fun to play around with on the testnets!)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.