1

I am trying to understand assembly a little better, so I've been paying attention to the assembly output from CDB when I debug my code. My platform is an Intel Xeon with Windows 7.

The following C++ code:

int main()
{
    int a = 30;
    int b = 0;
    b = ++a;
    return 0;
}

produces the following assembly for the line with the increment operator:

            b = ++a;
0x13f441023  <+0x0013>         mov     eax,dword ptr [rsp]
0x13f441026  <+0x0016>         inc     eax
0x13f441028  <+0x0018>         mov     dword ptr [rsp],eax  //Move eax to some memory address
0x13f44102b  <+0x001b>         mov     eax,dword ptr [rsp]  //Move it back to eax?
0x13f44102e  <+0x001e>         mov     dword ptr [rsp+4],eax

My question is, what is the purpose of moving the value in eax to memory, then immediately moving the same value back into eax, as indicated by the comments? Is this for thread safety, or just some artifact of a debug build?

4
  • 5
    Which optimization level did you specify with compiling? Looking at code built with -O0 is kind of pointless, at least if you're going to scrutinize its efficiency. Commented Aug 17, 2015 at 13:02
  • I'm less concerned with efficiency, just curious when this extra mov would ever be useful. On a side note, optimizing with -02 removes the redundant operation. Commented Aug 17, 2015 at 13:12
  • 2
    It is part of the direct meaning of b=++a. It is effectively the same as b=(a+=1); so you compute a+1 and store that into a and then copy from a to b. An unoptimized compile is not supposed to notice that the value just stored into a is still in eax, so you don't need to read it back from a when copying a to b. An unoptimized compile forgets the register contents after each step. Commented Aug 17, 2015 at 13:19
  • 1
    Also notice that the first three instructions do the ++a and the final 2 do the b = a (where a already has the incremented value). Due to no optimization, the compiler didn't eliminate the reload of eax. Commented Aug 17, 2015 at 13:20

2 Answers 2

4

The compiler initially translates your instructions into assembly using static single assignment values (SSA), meaning that each operation gets a temporary value to store its result. Only at a later backend stage, these values would be translated into machine registers according to your target machine, and possibly into memory locations if necessary (explicitly required or spilled due to lack of registers).

Between these stages, the optimizer may eliminate partial values, but initially ++a is one operation, and assigning a (post increment) into b is a second operation. Since a and b are both local variables, they will be stored on the stack (and must be visible there, in case you step with a debugger for example), a will reside in [rsp] and b in [rsp+4].

So your compiler, at some point, probably has (in some intermediate representation):

    value1 = a
    value2 = value1 + 1
    a = value2  //self increment
    b = a

Or something similar. a and b must be memory resident, but the operations will usually be done on registers, so at first the compiler does -

    value1 = a
    value2 = value1 + 1
0x13f441023  <+0x0013>         mov     eax,dword ptr [rsp]
0x13f441026  <+0x0016>         inc     eax

    a = value2
0x13f441028  <+0x0018>         mov     dword ptr [rsp],eax

    b = a
0x13f44102b  <+0x001b>         mov     eax,dword ptr [rsp]
0x13f44102e  <+0x001e>         mov     dword ptr [rsp+4],eax 

Note that the intermediate values were kept in a register - in a normal compilation, they would have probably get eliminated altogether by one of the optimization passes (prior to register assignment and code generation).

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that makes a lot of sense.
0

just some artifact of a debug build?

Yes, just some artifact of a debug build (actually from an unoptimized build)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.