7

I wrote a simple contract with a single state variable with type uint and the generated assembly code is quite straight forward. But when I changed the type to uint32 the generated assembly becomes different altogether. Also for uint32 if I check the 'Enable Optimizer' option the generated assembly code gets changed.

Questions:

  1. In case of uint32 why the generated assembly is getting changed? Also if you can please explain the generated assembly itself.
  2. The assembly codes for uint32 with optimizer on and off are different. Why is it so? I understand that the optimizer is optimizing the performance that is why the assembly instructions are getting smaller but my question is how?

For your reference I am providing the contract along with the generated assembly code segment.

Contract

pragma solidity ^0.4.0;

contract Test 
{
   uint32 value = 10;
}

Generated assembly with optimizer off

PUSH1 0x60 
PUSH1 0x40 
MSTORE 
PUSH1 0xA 
PUSH1 0x0 
PUSH1 0x0 
PUSH2 0x100 
EXP 
DUP2 
SLOAD 
DUP2 
PUSH4 0xFFFFFFFF 
MUL 
NOT 
AND 
SWAP1 
DUP4 
PUSH4 0xFFFFFFFF 
AND 
MUL 
OR 
SWAP1 
SSTORE 
POP 
CALLVALUE 
PUSH1 0x0 
JUMPI 

Generated assembly with optimizer on

PUSH1 0x60
PUSH1 0x40
MSTORE
PUSH1 0x0
DUP1
SLOAD
PUSH4 0xFFFFFFFF
NOT
AND
PUSH1 0xA
OR
SWAP1
SSTORE
CALLVALUE
PUSH1 0x0
JUMPI 

Thanks, Shamik.

  • Hello. I wonder why you need to know this, since all efforts are done to avoid this very low level code by creating some kind of interfaces such as ABI or bytecode. – FrenchieiSverige Feb 1 '17 at 15:58
  • We were trying to investigate the generated opcodes and found this segment. May you please explain why is it running in this manner? – Shamik Feb 2 '17 at 15:00
  • uint is equivalent to uint256 but even in that case the generated bytecode still seems different, interesting... – eth Feb 3 '17 at 15:29
  • We have understood the storing mechanism when optimizer is on. But why this SLOAD is required at the beginning? As per specification the storage is 32 bytes long (both key and value) and defaulting with all zeroes. – Shamik Feb 9 '17 at 7:56
  • please have a look at ethereum.stackexchange.com/questions/15382/… – Badr Bellaj Jul 31 '17 at 21:53
4

Others have pointed in the right direction, but let me try to specifically answer the questions.

First, each 256-bit word of contract storage is very expensive. When a contract is using smaller variables, such as uint32 (32 bits), then Solidity will try to pack multiple variables into one storage word. Most of what you see here is the compiler first inserting the apparatus to pack and unpack the uint32 variables from storage, and then the optimiser taking it all out again since it's not needed in this really simple case.

Useful labels

I'm going to define some labels to keep track of things:

VALUE = value, the uint32 in your contract = 0x0a (ten)
MASK  = 0xFFFFFFFF This is 32 bits of ones, and matches the width of a uint32.
S[0]  = the contract storage location zero: 256 bits wide.
WORD  = the contents of S[0]: potentially packed storage of uint32s
N     = the shift: number of bytes from the right-hand-side of WORD where VALUE is stored within S[0]

Pictorially, here's an example 32 bytes of the S[0] WORD, with the VALUE storage marked with VVVV, shifted by N bytes (12 here) within S[0].

0123456789abcdef0123456789abcdef
................VVVV............
                    <---- N ----

Q1 In detail

First is just memory management preamble the compiler always inserts. The top of used memory is intially 0x60 and this value is stored at 0x40 for later reference:

PUSH1 0x60 
PUSH1 0x40 
MSTORE

Next is your assignment value, 10 in uint32 value = 10;

PUSH1 0xA     // VALUE

Now, the compiler knows it has to place the variable into storage, and that it is shorter than a whole word - a uint32 rather than 256 bits. Therefore it makes a mask and shifts the mask by N bytes to where in the 256-bit word the variable is stored. Ignore the fact that N=0 for now; it could be different from 0 in general.

PUSH1 0x0     // the zero in S[0].
PUSH1 0x0     // the shift, N
PUSH2 0x100   // One byte shift left is 8 bits
EXP           // Calculates 0x100 ^ N, i.e. 8*N-bits shifter
DUP2          // Get the storage slot number [0]
SLOAD         // Load WORD from S[0]
DUP2          // Get the shifter we calculated earlier
PUSH4 0xFFFFFFFF // MASK
MUL           // Shift the mask along by N bytes
NOT           // Invert the mask - every bit is flipped.
AND           // Apply the mask. All bits of WORD within the 32 bits of your variable are set to zero; all bits outside are unaffected.

Now we have the original WORD of S[0] in memory, with the location of VALUE zeroed out. It needs to be zeroed out since we can't rely on it being zero if it has been set by a previous invocation of the contract.

SWAP1         // Retrieve the shifter we calculated earlier
DUP4          // VALUE is retrieved
PUSH4 0xFFFFFFFF // MASK again
AND           // Ensure VALUE is truncated to 32 bits since it is uint32
MUL           // Shift value left by N bytes
OR            // logical or VALUE into WORD. The corresponding WORD bits are zero, so this just sets them to VALUE
SWAP1 
SSTORE        // Store WORD back into memory at S[0]

Now we're all done, with the 32 bits of VALUE inserted at the right place into S[0] and all the other bits of S[0] unaffected.

Q2 Optimisation

Now, the above describes the general case for any shift, N. However, in this case N==0 and that is a much easier situation, so the optimiser is able to find some simplifications.

  1. The optimiser recognises that 0x100 ^ 0 = 1, and that multiplying by 1 is a no-op. So it removes all the code associated with shifting MASK or VALUE. No shift is required on this occasion since we have only one variable.

  2. The optimiser also recognises that the stack VALUE pushed (0x0a) is less than 32 bits wide, so it does not need to apply MASK to this.

This is sufficient to produce the final optimised code. It does a good job in this case, and produces much cleaner code.

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.