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.
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.
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.
uint
is equivalent touint256
but even in that case the generated bytecode still seems different, interesting... – eth♦ Feb 3 '17 at 15:29