The compiler optimized it, he previously calculated the answer and simply set the result. If you want the compiler to make an addition, you cannot let it βseeβ the constants that you feed it.
If you compile this code yourself as an object (gcc -O2 -c test_add.c -o test_add.o) then you will force the compiler to generate the add code. But the operands will be registers or on the stack.
int test_add ( int a, int b ) { return(a+b); }
Then, if you call it from the code in a separate source (gcc -O2 -c test.c -o test.o), you will see that two operands will be forced into the function.
extern int test_add ( int, int ); int test ( void ) { return(test_add(2,2)); }
and you can parse both of these objects (objdump -D test.o, objdump -D test_add.o)
When you do something simple in one file
int main ( void ) { int a,b,c; a=2; b=2; c=a+b; return(0); }
The compiler can optimize your code into one of several equivalents. My example is here, does nothing, math and results have no purpose, they are not used, so they can simply be deleted as dead code. This operation did it.
int main ( void ) { int c; c=4; return(0); }
But this is also a legitimate optimization of the above code.
int main ( void ) { return(0); }
EDIT:
Where is calc = 2 + 2 located?
I think that
movl $4,-12(%rbp)
Is 2 + 2 (the answer is computed and just put in calc, which is on the stack.
movl $0,-8(%rbp)
I assume 0 in your return (0);
The actual math of adding two numbers has been optimized.