This is the C Program

#include <stdio.h>
9
10 int main(void)
11 {
12 int w, x, y, z;
13
14 printf("Enter two integers: ");
15 scanf("%i %i", &w, &x);
16 y = w + x;
17 z = w - x;
18 printf("sum = %i, difference = %i\n", y, z);
19
20 return 0;
21 }


This is assembly written by Bob (text book author).



38 movl w(%rbp), %esi # y = w

39 addl x(%rbp), %esi # y += x

45 movl w(%rbp), %edx # z = w

46 subl x(%rbp), %edx # z -= x



And this is the ASM generated by GCC from the C program.


24 movl -4(%rbp), %edx # load w

25 movl -8(%rbp), %eax # load x

26 leal (%rdx,%rax), %eax # eax <- w + x
27 movl %eax, -12(%rbp) # y = w + x;

28 movl -4(%rbp), %edx # load w

29 movl -8(%rbp), %eax # load x

30 movl %edx, %ecx # ecx <- w

31 subl %eax, %ecx # eax <- w - x



Its obvious the code generated by Bob is efficient more so than the GCC compiler. However, I don't understand why the calculations are different.
Does it have to do with the order of operations of the two different programs? Is it because on line 26 gcc performs addition on 64-bit registers then places the result in a 32-bit register? And how on earth is line 27 addition?
Posted on 2010-11-19 15:10:39 by bray
gcc abuses lea as an add with 3 operands. Lea is meant to calculate addresses, so it can use 64-bit registers only.
Line 27 is not an addition, it just copies the previously calculated w + x in eax to the y variable.

gcc is apparently having some problems working around the limitation that you only have 3 volatile registers, namely eax, ecx and edx. All others need to be preserved. So it loads the variables from memory more than once.
I think it could be slightly smarter with the lea for example, by not re-using eax for the result, but using ecx (which would be the point of using lea instead of a normal add anyway, not overwriting one of your source operands). Then the second mov on line 29 could be skipped. Likewise the mov on line 30 makes no sense at all, it could have done subl %eax, %edx right away.
I would think that the latest gcc with the maximum optimization settings should produce something better than this.
Posted on 2010-11-20 04:33:10 by Scali
Hi Scali;

So the results of the calculations differ than the text book source
because lea is required to use 64-bit registers? I may understand...
I'll try to elaborate.
On lines 24 and 25, w and x are loaded into edx and eax. Then leal takes
the entire 64-bit register and adds them together. Bob mixes his addl
mnemonic with both 64-bit and 32-bit registers. So is the result of the addition
different because of my premise that the addition of two 64-bit registers is
different than adding a 64-bit register to a 32-bit register?
Posted on 2010-11-22 13:49:16 by bray
I'm not sure what you mean by "the result of the calculations differ"?

I mean, the source code says this:
y = w + x;
z = w - x;

The author's code does this:
y = w
y += x

z = w
z -= x

That is the same, is it not?

And the gcc code does this:
y = w + x;
eax <- w - x

Now, there may be a line missing there, so we don't actually see eax being stored to z... but both pieces of code perform w + x and w - x, right?
They perform it in a different way, but I don't see how they would get different results?

Bob mixes his addl mnemonic with both 64-bit and 32-bit registers.


In Bob's case, they are only used to address the variables on stack. It's indexed memory addressing. You always use 64-bit addresses in 64-bit mode.
In gcc's case, they use lea to perform an add, as stated before. The extra 32-bits can just be discarded, as they won't affect the result.
Posted on 2010-11-22 15:16:38 by Scali
Pardon me, the C program or its resultant assembly code, give different results in the case of an overflow.
Posted on 2010-11-22 15:49:19 by bray
I read up on the instruction and found that LEA does not check for overflow. Herp a derp.
Posted on 2010-11-24 12:43:33 by bray
None of this code checks for overflow...
There should be no difference...
Posted on 2013-03-27 11:58:27 by Scali